From 9c358df52a4d59ce526aef0410b708bfb765208e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cramfox=E2=80=9D?= <“kasey@n0.computer”> Date: Fri, 5 Dec 2025 16:47:01 -0500 Subject: [PATCH 1/9] add and re-export any quinn types that we use in the public API --- iroh/src/endpoint.rs | 16 ++++++++------ iroh/src/endpoint/connection.rs | 39 +++++++++++++++++---------------- iroh/src/endpoint/quic.rs | 9 ++++---- 3 files changed, 34 insertions(+), 30 deletions(-) diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index 1aa3882bc13..252fdb2bd63 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -58,14 +58,16 @@ pub use self::{ RemoteEndpointIdError, ZeroRttStatus, }, quic::{ - AcceptBi, AcceptUni, AckFrequencyConfig, AeadKey, ApplicationClose, Chunk, ClosedStream, - ConnectionClose, ConnectionError, ConnectionStats, Controller, ControllerFactory, - CryptoError, CryptoServerConfig, ExportKeyingMaterialError, FrameStats, HandshakeTokenKey, - IdleTimeout, MtuDiscoveryConfig, OpenBi, OpenUni, PathStats, QuicTransportConfig, + AcceptBi, AcceptUni, AckFrequencyConfig, AeadKey, ApplicationClose, BloomTokenLog, Chunk, + ClosedStream, ConnectionClose, ConnectionError, ConnectionStats, Controller, + ControllerFactory, ControllerMetrics, CryptoError, CryptoServerConfig, Dir, + ExportKeyingMaterialError, FrameStats, FrameType, HandshakeTokenKey, IdleTimeout, + MtuDiscoveryConfig, NoneTokenLog, OpenBi, OpenUni, PathStats, QuicTransportConfig, ReadDatagram, ReadError, ReadExactError, ReadToEndError, RecvStream, ResetError, - RetryError, SendDatagramError, SendStream, ServerConfig, Side, StoppedError, StreamId, - TransportError, TransportErrorCode, UdpStats, UnsupportedVersion, VarInt, - VarIntBoundsExceeded, WeakConnectionHandle, WriteError, Written, + RetryError, RttEstimator, SendDatagramError, SendStream, ServerConfig, Side, StoppedError, + StreamId, TimeSource, TokenLog, TransportError, TransportErrorCode, UdpStats, + UnsupportedVersion, ValidationTokenConfig, VarInt, VarIntBoundsExceeded, WriteError, + Written, }, }; pub use crate::magicsock::transports::TransportConfig; diff --git a/iroh/src/endpoint/connection.rs b/iroh/src/endpoint/connection.rs index f27e3d8a0c3..ce9aeebb9b6 100644 --- a/iroh/src/endpoint/connection.rs +++ b/iroh/src/endpoint/connection.rs @@ -32,16 +32,19 @@ use n0_error::{e, stack_error}; use n0_future::{TryFutureExt, future::Boxed as BoxFuture, time::Duration}; use n0_watcher::Watcher; use pin_project::pin_project; -use quinn::{ - AcceptBi, AcceptUni, ConnectionError, ConnectionStats, OpenBi, OpenUni, ReadDatagram, - RetryError, SendDatagramError, ServerConfig, Side, VarInt, WeakConnectionHandle, -}; +use quinn::WeakConnectionHandle; use quinn_proto::PathId; use tracing::warn; use crate::{ Endpoint, - endpoint::AfterHandshakeOutcome, + endpoint::{ + AfterHandshakeOutcome, + quic::{ + AcceptBi, AcceptUni, ConnectionError, ConnectionStats, OpenBi, OpenUni, ReadDatagram, + RetryError, SendDatagram, SendDatagramError, ServerConfig, Side, VarInt, + }, + }, magicsock::{ RemoteStateActorStoppedError, remote_map::{PathInfoList, PathsWatcher}, @@ -822,20 +825,18 @@ impl Connection { self.inner.send_datagram(data) } - // TODO: It seems `SendDatagram` is not yet exposed by quinn. This has been fixed - // upstream and will be in the next release. - // /// Transmits `data` as an unreliable, unordered application datagram - // /// - // /// Unlike [`send_datagram()`], this method will wait for buffer space during congestion - // /// conditions, which effectively prioritizes old datagrams over new datagrams. - // /// - // /// See [`send_datagram()`] for details. - // /// - // /// [`send_datagram()`]: Connection::send_datagram - // #[inline] - // pub fn send_datagram_wait(&self, data: bytes::Bytes) -> SendDatagram<'_> { - // self.inner.send_datagram_wait(data) - // } + /// Transmits `data` as an unreliable, unordered application datagram + /// + /// Unlike [`send_datagram()`], this method will wait for buffer space during congestion + /// conditions, which effectively prioritizes old datagrams over new datagrams. + /// + /// See [`send_datagram()`] for details. + /// + /// [`send_datagram()`]: Connection::send_datagram + #[inline] + pub fn send_datagram_wait(&self, data: bytes::Bytes) -> SendDatagram<'_> { + self.inner.send_datagram_wait(data) + } /// Computes the maximum size of datagrams that may be passed to [`send_datagram`]. /// diff --git a/iroh/src/endpoint/quic.rs b/iroh/src/endpoint/quic.rs index cde0e5ae98a..1614c2163a3 100644 --- a/iroh/src/endpoint/quic.rs +++ b/iroh/src/endpoint/quic.rs @@ -15,14 +15,15 @@ pub use quinn::{ AcceptBi, AcceptUni, AckFrequencyConfig, ApplicationClose, Chunk, ClosedStream, ConnectionClose, ConnectionError, ConnectionStats, MtuDiscoveryConfig, OpenBi, OpenUni, PathStats, ReadDatagram, ReadError, ReadExactError, ReadToEndError, RecvStream, ResetError, - RetryError, SendDatagramError, SendStream, ServerConfig, Side, StoppedError, StreamId, VarInt, - VarIntBoundsExceeded, WeakConnectionHandle, WriteError, + RetryError, SendDatagram, SendDatagramError, SendStream, ServerConfig, Side, StoppedError, + StreamId, VarInt, VarIntBoundsExceeded, WriteError, }; #[cfg(feature = "qlog")] pub use quinn::{QlogConfig, QlogFactory, QlogFileFactory}; pub use quinn_proto::{ - FrameStats, IdleTimeout, TransportError, TransportErrorCode, UdpStats, Written, - congestion::{Controller, ControllerFactory}, + BloomTokenLog, Dir, FrameStats, FrameType, IdleTimeout, NoneTokenLog, RttEstimator, TimeSource, + TokenLog, TransportError, TransportErrorCode, UdpStats, ValidationTokenConfig, Written, + congestion::{Controller, ControllerFactory, ControllerMetrics}, crypto::{ AeadKey, CryptoError, ExportKeyingMaterialError, HandshakeTokenKey, ServerConfig as CryptoServerConfig, UnsupportedVersion, From 3368854511d4e131c8d30525948e65e22f1118bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cramfox=E2=80=9D?= <“kasey@n0.computer”> Date: Tue, 9 Dec 2025 23:16:49 -0500 Subject: [PATCH 2/9] feat: newtype `quinn::ServerConfig` `ServerConfig` is used in the public API in `Incoming::accept_with`. `quinn::ServerConfig` takes a `TransportConfig`. However, we want to maintain control over the `TransportConfig` fields, so we new typed it to `QuicTransportConfig`. So, we needed to new type `quinn::ServerConfig`, which ensures setting the `QuicTransportConfig` correctly. This also organizes the quinn imports and labels where each type comes from. --- iroh/src/endpoint.rs | 22 +-- iroh/src/endpoint/connection.rs | 12 +- iroh/src/endpoint/quic.rs | 254 ++++++++++++++++++++++++++++++-- 3 files changed, 255 insertions(+), 33 deletions(-) diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index 252fdb2bd63..173164b2359 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -58,16 +58,16 @@ pub use self::{ RemoteEndpointIdError, ZeroRttStatus, }, quic::{ - AcceptBi, AcceptUni, AckFrequencyConfig, AeadKey, ApplicationClose, BloomTokenLog, Chunk, - ClosedStream, ConnectionClose, ConnectionError, ConnectionStats, Controller, + AcceptBi, AcceptUni, AckFrequencyConfig, AeadKey, ApplicationClose, Chunk, ClosedStream, + Codec, ConnectionClose, ConnectionError, ConnectionId, ConnectionStats, Controller, ControllerFactory, ControllerMetrics, CryptoError, CryptoServerConfig, Dir, - ExportKeyingMaterialError, FrameStats, FrameType, HandshakeTokenKey, IdleTimeout, - MtuDiscoveryConfig, NoneTokenLog, OpenBi, OpenUni, PathStats, QuicTransportConfig, - ReadDatagram, ReadError, ReadExactError, ReadToEndError, RecvStream, ResetError, - RetryError, RttEstimator, SendDatagramError, SendStream, ServerConfig, Side, StoppedError, - StreamId, TimeSource, TokenLog, TransportError, TransportErrorCode, UdpStats, - UnsupportedVersion, ValidationTokenConfig, VarInt, VarIntBoundsExceeded, WriteError, - Written, + ExportKeyingMaterialError, FrameStats, FrameType, HandshakeTokenKey, HeaderKey, + IdleTimeout, Keys, MtuDiscoveryConfig, OpenBi, OpenUni, PacketKey, PathId, PathStats, + QuicConnectError, QuicTransportConfig, ReadDatagram, ReadError, ReadExactError, + ReadToEndError, RecvStream, ResetError, RttEstimator, SendDatagramError, SendStream, + ServerConfig, Session, Side, StoppedError, StreamId, TimeSource, TokenLog, TokenReuseError, + TransportError, TransportErrorCode, TransportParameters, UdpStats, UnsupportedVersion, + ValidationTokenConfig, VarInt, VarIntBoundsExceeded, WriteError, Written, }, }; pub use crate::magicsock::transports::TransportConfig; @@ -471,11 +471,11 @@ struct StaticConfig { impl StaticConfig { /// Create a [`quinn::ServerConfig`] with the specified ALPN protocols. - fn create_server_config(&self, alpn_protocols: Vec>) -> ServerConfig { + fn create_server_config(&self, alpn_protocols: Vec>) -> quinn::ServerConfig { let quic_server_config = self .tls_config .make_server_config(alpn_protocols, self.keylog); - let mut server_config = ServerConfig::with_crypto(Arc::new(quic_server_config)); + let mut server_config = quinn::ServerConfig::with_crypto(Arc::new(quic_server_config)); server_config.transport_config(self.transport_config.to_arc()); server_config diff --git a/iroh/src/endpoint/connection.rs b/iroh/src/endpoint/connection.rs index ce9aeebb9b6..562dfe2a46a 100644 --- a/iroh/src/endpoint/connection.rs +++ b/iroh/src/endpoint/connection.rs @@ -21,7 +21,6 @@ use std::{ future::{Future, IntoFuture}, net::{IpAddr, SocketAddr}, pin::Pin, - sync::Arc, task::Poll, }; @@ -42,7 +41,7 @@ use crate::{ AfterHandshakeOutcome, quic::{ AcceptBi, AcceptUni, ConnectionError, ConnectionStats, OpenBi, OpenUni, ReadDatagram, - RetryError, SendDatagram, SendDatagramError, ServerConfig, Side, VarInt, + SendDatagram, SendDatagramError, ServerConfig, Side, VarInt, }, }, magicsock::{ @@ -106,12 +105,9 @@ impl Incoming { /// See [`accept()`] for more details. /// /// [`accept()`]: Incoming::accept - pub fn accept_with( - self, - server_config: Arc, - ) -> Result { + pub fn accept_with(self, server_config: ServerConfig) -> Result { self.inner - .accept_with(server_config) + .accept_with(server_config.to_arc()) .map(|conn| Accepting::new(conn, self.ep)) } @@ -126,7 +122,7 @@ impl Incoming { /// /// Errors if `remote_address_validated()` is true. #[allow(clippy::result_large_err)] - pub fn retry(self) -> Result<(), RetryError> { + pub fn retry(self) -> Result<(), quinn::RetryError> { self.inner.retry() } diff --git a/iroh/src/endpoint/quic.rs b/iroh/src/endpoint/quic.rs index 1614c2163a3..514e199b265 100644 --- a/iroh/src/endpoint/quic.rs +++ b/iroh/src/endpoint/quic.rs @@ -8,26 +8,86 @@ #[cfg(feature = "qlog")] use std::path::Path; -use std::{sync::Arc, time::Duration}; -// Missing still: SendDatagram and ConnectionClose::frame_type's Type. -pub use quinn::{ - AcceptBi, AcceptUni, AckFrequencyConfig, ApplicationClose, Chunk, ClosedStream, - ConnectionClose, ConnectionError, ConnectionStats, MtuDiscoveryConfig, OpenBi, OpenUni, - PathStats, ReadDatagram, ReadError, ReadExactError, ReadToEndError, RecvStream, ResetError, - RetryError, SendDatagram, SendDatagramError, SendStream, ServerConfig, Side, StoppedError, - StreamId, VarInt, VarIntBoundsExceeded, WriteError, +use std::{ + net::{SocketAddrV4, SocketAddrV6}, + sync::Arc, + time::Duration, }; + #[cfg(feature = "qlog")] pub use quinn::{QlogConfig, QlogFactory, QlogFileFactory}; + +/// `quinn` types that are used in the public iroh API. +// Each type is notated with the iroh type or quinn type that uses it. +pub use quinn::{ + AcceptBi, // iroh::endpoint::Connection + AcceptUni, // iroh::endpoint::Connection + AckFrequencyConfig, // iroh::endpoint::quic::QuicTransportConfig + ClosedStream, // iroh::protocol::AcceptError, quinn::RecvStream, quinn::SendStream + ConnectionError, // iroh::endpoint::ConnectError + ConnectionStats, // iroh::endpoint::Connection + Dir, // quinn::StreamId + IdleTimeout, // iroh::endpoint::quic::QuicTransportConfig + MtuDiscoveryConfig, // iroh::endpoint::quic::QuicTransportConfig + OpenBi, // iroh::endpoint::Connection + OpenUni, // iroh::endpoint::Connection + PathStats, // iroh::magicsock::remote_map::remote_state::PathInfo + ReadDatagram, // iroh::endpoint::Connection + ReadError, // quinn::RecvStream + ReadExactError, // quinn::RecvStream + ReadToEndError, // quinn::RecvStream + RecvStream, // quinn::AcceptBi, quinn::AcceptUni, quinn::OpenBi, quinn::OpenUni + ResetError, // quinn::RecvStream + SendDatagram, // iroh::endpoint::Connection + SendDatagramError, // iroh::endpoint::Connection + SendStream, // quinn::AcceptBi, quinn::OpenUni + Side, // iroh::endpoint::Connection, quinn::StreamId, + StoppedError, // quinn::SendStream + StreamId, // quinn::RecvStream + VarInt, // various + VarIntBoundsExceeded, // quinn::VarInt, quinn::IdleTimeout + WriteError, // quinn::SendStream + Written, // quinn::SendStream +}; +/// `quinn_proto` types that are used in the public iroh API. +// Each type is notated with the iroh type or quinn type that uses it. pub use quinn_proto::{ - BloomTokenLog, Dir, FrameStats, FrameType, IdleTimeout, NoneTokenLog, RttEstimator, TimeSource, - TokenLog, TransportError, TransportErrorCode, UdpStats, ValidationTokenConfig, Written, - congestion::{Controller, ControllerFactory, ControllerMetrics}, + ApplicationClose, // quinn::ConnectionError + Chunk, // quinn::RecvStream + ConnectError as QuicConnectError, // iroh::endpoint::ConnectWithOptsError + ConnectionClose, // quinn::ConnectionError + ConnectionId, // quinn_proto::crypto::ServerConfig + FrameStats, // quinn::ConnectionStats + FrameType, // quinn_proto::TransportError + PathId, // quinn_proto::crypto::PacketKey + RttEstimator, // quinn_proto::congestion::Controller + TimeSource, // iroh::endpoint::quic::ServerConfig + TokenLog, // quinn::ValidationTokenConfig + TokenReuseError, // quinn::TokenLog + TransportError, // quinn::ConnectionError + TransportErrorCode, // quinn_proto::TransportError + UdpStats, // quinn::ConnectionStats + ValidationTokenConfig, // iroh::endpoint::quic::::ServerConfig + coding::Codec, // quinn_proto::TransportErrorCode, quinn::StreamId + congestion::{ + Controller, // iroh::endpoint::Connection + ControllerFactory, // iroh::endpoint::quic::QuicTransportConfig + ControllerMetrics, // quinn_proto::congestion::Controller + }, crypto::{ - AeadKey, CryptoError, ExportKeyingMaterialError, HandshakeTokenKey, - ServerConfig as CryptoServerConfig, UnsupportedVersion, + AeadKey, // quinn::HandshakeTokenKey + CryptoError, // quinn_proto::crypto::CryptoError, quinn_proto::crypto::PacketKey + ExportKeyingMaterialError, // iroh::endpoint::Connection + HandshakeTokenKey, // iroh::endpoint::quic::ServerConfig + HeaderKey, // quinn_proto::crypto::Keys + Keys, // quinn_proto::crypto::Session + PacketKey, // quinn_proto::crypto::Keys + ServerConfig as CryptoServerConfig, // iroh::endpoint::quic::ServerConfig + Session, // quinn_proto::crypto::ServerConfig + UnsupportedVersion, // quinn_proto::ConnectError }, + transport_parameters::TransportParameters, // quinn_proto::crypot::ServerConfig }; use tracing::warn; @@ -72,7 +132,7 @@ impl Default for QuicTransportConfig { impl QuicTransportConfig { /// Return an `Arc`-d [`quinn::TransportConfig`] - pub(super) fn to_arc(&self) -> Arc { + pub(crate) fn to_arc(&self) -> Arc { Arc::new(self.0.clone()) } @@ -485,3 +545,169 @@ impl QuicTransportConfig { self } } + +/// Parameters governing incoming connections +/// +/// Default values should be suitable for most internet applications. +// Note: used in `iroh::endpoint::connection::Incoming::accept_with` +// This is new-typed since `quinn::ServerConfig` takes a `TransportConfig`, which we new-type as a `QuicTransportConfig` +#[derive(Debug, Clone)] +pub struct ServerConfig { + inner: quinn::ServerConfig, + transport: QuicTransportConfig, +} + +impl ServerConfig { + /// Transport configuration to use for incoming connections + pub fn transport(&self) -> QuicTransportConfig { + self.transport.clone() + } + + /// TLS configuration used for incoming connections + /// + /// Must be set to use TLS 1.3 only. + pub fn crypto(&self) -> Arc { + self.inner.crypto.clone() + } + + /// Configuration for sending and handling validation tokens + pub fn validation_token(&self) -> ValidationTokenConfig { + self.inner.validation_token.clone() + } + + pub(crate) fn to_arc(&self) -> Arc { + Arc::new(self.inner.clone()) + } + + /// Create a default config with a particular handshake token key + pub fn new(crypto: Arc, token_key: Arc) -> Self { + let mut inner = quinn::ServerConfig::new(crypto, token_key); + let transport = QuicTransportConfig::default(); + inner.transport_config(transport.to_arc()); + Self { inner, transport } + } + + /// Set a custom [`TransportConfig`] + pub fn transport_config(&mut self, transport: QuicTransportConfig) -> &mut Self { + self.inner.transport_config(transport.to_arc()); + self.transport = transport; + self + } + + /// Set a custom [`ValidationTokenConfig`] + pub fn validation_token_config( + &mut self, + validation_token: ValidationTokenConfig, + ) -> &mut Self { + self.inner.validation_token_config(validation_token); + self + } + + /// Private key used to authenticate data included in handshake tokens + pub fn token_key(&mut self, value: Arc) -> &mut Self { + self.inner.token_key(value); + self + } + + /// Duration after a retry token was issued for which it's considered valid + /// + /// Defaults to 15 seconds. + pub fn retry_token_lifetime(&mut self, value: Duration) -> &mut Self { + self.inner.retry_token_lifetime(value); + self + } + + /// Whether to allow clients to migrate to new addresses + /// + /// Improves behavior for clients that move between different internet connections or suffer NAT + /// rebinding. Enabled by default. + pub fn migration(&mut self, value: bool) -> &mut Self { + self.inner.migration(value); + self + } + + /// The preferred IPv4 address that will be communicated to clients during handshaking + /// + /// If the client is able to reach this address, it will switch to it. + pub fn preferred_address_v4(&mut self, address: Option) -> &mut Self { + self.inner.preferred_address_v4(address); + self + } + + /// The preferred IPv6 address that will be communicated to clients during handshaking + /// + /// If the client is able to reach this address, it will switch to it. + pub fn preferred_address_v6(&mut self, address: Option) -> &mut Self { + self.inner.preferred_address_v6(address); + self + } + + /// Maximum number of [`Incoming`][crate::Incoming] to allow to exist at a time + /// + /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt + /// is received and stops existing when the application either accepts it or otherwise disposes + /// of it. While this limit is reached, new incoming connection attempts are immediately + /// refused. Larger values have greater worst-case memory consumption, but accommodate greater + /// application latency in handling incoming connection attempts. + /// + /// The default value is set to 65536. With a typical Ethernet MTU of 1500 bytes, this limits + /// memory consumption from this to under 100 MiB--a generous amount that still prevents memory + /// exhaustion in most contexts. + pub fn max_incoming(&mut self, max_incoming: usize) -> &mut Self { + self.inner.max_incoming(max_incoming); + self + } + + /// Maximum number of received bytes to buffer for each [`Incoming`][crate::Incoming] + /// + /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt + /// is received and stops existing when the application either accepts it or otherwise disposes + /// of it. This limit governs only packets received within that period, and does not include + /// the first packet. Packets received in excess of this limit are dropped, which may cause + /// 0-RTT or handshake data to have to be retransmitted. + /// + /// The default value is set to 10 MiB--an amount such that in most situations a client would + /// not transmit that much 0-RTT data faster than the server handles the corresponding + /// [`Incoming`][crate::Incoming]. + pub fn incoming_buffer_size(&mut self, incoming_buffer_size: u64) -> &mut Self { + self.inner.incoming_buffer_size(incoming_buffer_size); + self + } + + /// Maximum number of received bytes to buffer for all [`Incoming`][crate::Incoming] + /// collectively + /// + /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt + /// is received and stops existing when the application either accepts it or otherwise disposes + /// of it. This limit governs only packets received within that period, and does not include + /// the first packet. Packets received in excess of this limit are dropped, which may cause + /// 0-RTT or handshake data to have to be retransmitted. + /// + /// The default value is set to 100 MiB--a generous amount that still prevents memory + /// exhaustion in most contexts. + pub fn incoming_buffer_size_total(&mut self, incoming_buffer_size_total: u64) -> &mut Self { + self.inner + .incoming_buffer_size_total(incoming_buffer_size_total); + self + } + + /// Object to get current [`SystemTime`] + /// + /// This exists to allow system time to be mocked in tests, or wherever else desired. + /// + /// Defaults to [`StdSystemTime`], which simply calls [`SystemTime::now()`](SystemTime::now). + pub fn time_source(&mut self, time_source: Arc) -> &mut Self { + self.inner.time_source(time_source); + self + } + + /// Create a server config with the given [`crypto::ServerConfig`] + /// + /// Uses a randomized handshake token key. + pub fn with_crypto(crypto: Arc) -> Self { + let mut inner = quinn::ServerConfig::with_crypto(crypto); + let transport = QuicTransportConfig::default(); + inner.transport_config(transport.to_arc()); + Self { inner, transport } + } +} From 2734fa2889a397d9211c9ede50e95052f22f99f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cramfox=E2=80=9D?= <“kasey@n0.computer”> Date: Tue, 9 Dec 2025 23:20:03 -0500 Subject: [PATCH 3/9] adjust `use quinn` imports to point to `iroh::endpoint::quic`, for types that are re-exported by iroh --- iroh/examples/0rtt.rs | 7 ++- iroh/examples/auth-hook.rs | 5 +- iroh/src/endpoint.rs | 47 +++++++++---------- iroh/src/endpoint/connection.rs | 47 +++++++++++++------ iroh/src/endpoint/hooks.rs | 3 +- iroh/src/endpoint/quic.rs | 36 ++++++++------ iroh/src/magicsock.rs | 15 +++--- iroh/src/magicsock/remote_map/remote_state.rs | 4 +- iroh/src/protocol.rs | 11 ++--- 9 files changed, 100 insertions(+), 75 deletions(-) diff --git a/iroh/examples/0rtt.rs b/iroh/examples/0rtt.rs index eb3fd564b64..eb81df39e8b 100644 --- a/iroh/examples/0rtt.rs +++ b/iroh/examples/0rtt.rs @@ -2,10 +2,13 @@ use std::{env, str::FromStr, time::Instant}; use clap::Parser; use data_encoding::HEXLOWER; -use iroh::{EndpointId, SecretKey, discovery::Discovery, endpoint::ZeroRttStatus}; +use iroh::{ + EndpointId, SecretKey, + discovery::Discovery, + endpoint::{RecvStream, SendStream, ZeroRttStatus}, +}; use n0_error::{Result, StackResultExt, StdResultExt}; use n0_future::StreamExt; -use quinn::{RecvStream, SendStream}; use tracing::{info, trace}; const PINGPONG_ALPN: &[u8] = b"0rtt-pingpong"; diff --git a/iroh/examples/auth-hook.rs b/iroh/examples/auth-hook.rs index 3201f03d53d..3e0d30908ed 100644 --- a/iroh/examples/auth-hook.rs +++ b/iroh/examples/auth-hook.rs @@ -113,12 +113,13 @@ mod auth { use iroh::{ Endpoint, EndpointAddr, EndpointId, - endpoint::{AfterHandshakeOutcome, BeforeConnectOutcome, Connection, EndpointHooks}, + endpoint::{ + AfterHandshakeOutcome, BeforeConnectOutcome, Connection, ConnectionError, EndpointHooks, + }, protocol::{AcceptError, ProtocolHandler}, }; use n0_error::{AnyError, Result, StackResultExt, StdResultExt, anyerr}; use n0_future::task::AbortOnDropHandle; - use quinn::ConnectionError; use tokio::{ sync::{mpsc, oneshot}, task::JoinSet, diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index 173164b2359..dd6b52ca239 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -44,7 +44,7 @@ use crate::{ mod connection; pub(crate) mod hooks; pub mod presets; -mod quic; +pub(crate) mod quic; pub use hooks::{AfterHandshakeOutcome, BeforeConnectOutcome, EndpointHooks}; @@ -55,7 +55,7 @@ pub use self::{ Accept, Accepting, AlpnError, AuthenticationError, Connecting, ConnectingError, Connection, ConnectionInfo, ConnectionState, HandshakeCompleted, Incoming, IncomingZeroRtt, IncomingZeroRttConnection, OutgoingZeroRtt, OutgoingZeroRttConnection, - RemoteEndpointIdError, ZeroRttStatus, + RemoteEndpointIdError, RetryError, ZeroRttStatus, }, quic::{ AcceptBi, AcceptUni, AckFrequencyConfig, AeadKey, ApplicationClose, Chunk, ClosedStream, @@ -64,10 +64,11 @@ pub use self::{ ExportKeyingMaterialError, FrameStats, FrameType, HandshakeTokenKey, HeaderKey, IdleTimeout, Keys, MtuDiscoveryConfig, OpenBi, OpenUni, PacketKey, PathId, PathStats, QuicConnectError, QuicTransportConfig, ReadDatagram, ReadError, ReadExactError, - ReadToEndError, RecvStream, ResetError, RttEstimator, SendDatagramError, SendStream, - ServerConfig, Session, Side, StoppedError, StreamId, TimeSource, TokenLog, TokenReuseError, - TransportError, TransportErrorCode, TransportParameters, UdpStats, UnsupportedVersion, - ValidationTokenConfig, VarInt, VarIntBoundsExceeded, WriteError, Written, + ReadToEndError, RecvStream, ResetError, RttEstimator, SendDatagram, SendDatagramError, + SendStream, ServerConfig, Session, Side, StoppedError, StreamId, TimeSource, TokenLog, + TokenReuseError, TransportError, TransportErrorCode, TransportParameters, UdpStats, + UnsupportedVersion, ValidationTokenConfig, VarInt, VarIntBoundsExceeded, WriteError, + Written, }, }; pub use crate::magicsock::transports::TransportConfig; @@ -527,7 +528,7 @@ pub enum ConnectWithOptsError { #[error("Unable to connect to remote")] Quinn { #[error(std_err)] - source: quinn_proto::ConnectError, + source: QuicConnectError, }, #[error("Internal consistency error")] InternalConsistencyError { @@ -1315,7 +1316,6 @@ mod tests { use n0_future::{BufferedStreamExt, StreamExt, stream, time}; use n0_tracing_test::traced_test; use n0_watcher::Watcher; - use quinn::ConnectionError; use rand::SeedableRng; use tokio::sync::oneshot; use tracing::{Instrument, debug_span, info, info_span, instrument}; @@ -1324,7 +1324,7 @@ mod tests { use crate::{ RelayMap, RelayMode, discovery::static_provider::StaticProvider, - endpoint::{ConnectOptions, Connection}, + endpoint::{ApplicationClose, ConnectOptions, Connection, ConnectionError}, protocol::{AcceptError, ProtocolHandler, Router}, test_utils::{QlogFileGroup, run_relay_server, run_relay_server_with}, }; @@ -1382,13 +1382,13 @@ mod tests { conn.close(7u8.into(), b"bye"); let res = conn.accept_uni().await; - assert_eq!(res.unwrap_err(), quinn::ConnectionError::LocallyClosed); + assert_eq!(res.unwrap_err(), ConnectionError::LocallyClosed); let res = stream.read_to_end(10).await; assert_eq!( res.unwrap_err(), quinn::ReadToEndError::Read(quinn::ReadError::ConnectionLost( - quinn::ConnectionError::LocallyClosed + ConnectionError::LocallyClosed )) ); info!("server test completed"); @@ -1419,11 +1419,10 @@ mod tests { info!("waiting for closed"); // Remote now closes the connection, we should see an error sometime soon. let err = conn.closed().await; - let expected_err = - quinn::ConnectionError::ApplicationClosed(quinn::ApplicationClose { - error_code: 7u8.into(), - reason: b"bye".to_vec().into(), - }); + let expected_err = ConnectionError::ApplicationClosed(ApplicationClose { + error_code: 7u8.into(), + reason: b"bye".to_vec().into(), + }); assert_eq!(err, expected_err); info!("opening new - expect it to fail"); @@ -1633,7 +1632,7 @@ mod tests { let ep1_nodeaddr = ep1.addr(); #[instrument(name = "client", skip_all)] - async fn connect(ep: Endpoint, dst: EndpointAddr) -> Result { + async fn connect(ep: Endpoint, dst: EndpointAddr) -> Result { info!(me = %ep.id().fmt_short(), "client starting"); let conn = ep.connect(dst, TEST_ALPN).await?; let mut send = conn.open_uni().await.anyerr()?; @@ -1662,7 +1661,7 @@ mod tests { let conn_closed = dbg!(ep2_connect.await.anyerr()??); assert!(matches!( conn_closed, - ConnectionError::ApplicationClosed(quinn::ApplicationClose { .. }) + ConnectionError::ApplicationClosed(ApplicationClose { .. }) )); Ok(()) @@ -1682,7 +1681,7 @@ mod tests { relay_map: RelayMap, node_addr_rx: oneshot::Receiver, qlog: Arc, - ) -> Result { + ) -> Result { let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(0u64); let secret = SecretKey::generate(&mut rng); let ep = Endpoint::builder() @@ -1758,7 +1757,7 @@ mod tests { let conn_closed = dbg!(client_task.await.anyerr()??); assert!(matches!( conn_closed, - ConnectionError::ApplicationClosed(quinn::ApplicationClose { .. }) + ConnectionError::ApplicationClosed(ApplicationClose { .. }) )); Ok(()) @@ -1776,7 +1775,7 @@ mod tests { async fn connect( relay_map: RelayMap, node_addr_rx: oneshot::Receiver, - ) -> Result { + ) -> Result { let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(0u64); let secret = SecretKey::generate(&mut rng); let ep = Endpoint::builder() @@ -1855,7 +1854,7 @@ mod tests { let conn_closed = dbg!(client_task.await.anyerr()??); assert!(matches!( conn_closed, - ConnectionError::ApplicationClosed(quinn::ApplicationClose { .. }) + ConnectionError::ApplicationClosed(ApplicationClose { .. }) )); Ok(()) @@ -1922,7 +1921,7 @@ mod tests { async fn accept( relay_map: RelayMap, node_addr_tx: oneshot::Sender, - ) -> Result { + ) -> Result { let secret = SecretKey::from([1u8; 32]); let ep = Endpoint::builder() .secret_key(secret) @@ -1970,7 +1969,7 @@ mod tests { let conn_closed = dbg!(server_task.await.anyerr()??); assert!(matches!( conn_closed, - ConnectionError::ApplicationClosed(quinn::ApplicationClose { .. }) + ConnectionError::ApplicationClosed(ApplicationClose { .. }) )); Ok(()) diff --git a/iroh/src/endpoint/connection.rs b/iroh/src/endpoint/connection.rs index 562dfe2a46a..589bfd2afed 100644 --- a/iroh/src/endpoint/connection.rs +++ b/iroh/src/endpoint/connection.rs @@ -32,7 +32,6 @@ use n0_future::{TryFutureExt, future::Boxed as BoxFuture, time::Duration}; use n0_watcher::Watcher; use pin_project::pin_project; use quinn::WeakConnectionHandle; -use quinn_proto::PathId; use tracing::warn; use crate::{ @@ -40,8 +39,9 @@ use crate::{ endpoint::{ AfterHandshakeOutcome, quic::{ - AcceptBi, AcceptUni, ConnectionError, ConnectionStats, OpenBi, OpenUni, ReadDatagram, - SendDatagram, SendDatagramError, ServerConfig, Side, VarInt, + AcceptBi, AcceptUni, ConnectionError, ConnectionStats, Controller, + ExportKeyingMaterialError, OpenBi, OpenUni, PathId, ReadDatagram, SendDatagram, + SendDatagramError, ServerConfig, Side, VarInt, }, }, magicsock::{ @@ -122,8 +122,10 @@ impl Incoming { /// /// Errors if `remote_address_validated()` is true. #[allow(clippy::result_large_err)] - pub fn retry(self) -> Result<(), quinn::RetryError> { - self.inner.retry() + pub fn retry(self) -> Result<(), RetryError> { + self.inner + .retry() + .map_err(|err| e!(RetryError { err, ep: self.ep })) } /// Ignores this incoming connection attempt, not sending any packet in response. @@ -164,6 +166,24 @@ impl IntoFuture for Incoming { } } +/// Error for attempting to retry an [`Incoming`] which already bears a token from a previous retry +#[stack_error(derive, add_meta, from_sources)] +#[error("retry() with validated Incoming")] +pub struct RetryError { + err: quinn::RetryError, + ep: Endpoint, +} + +impl RetryError { + /// Get the [`Incoming`] + pub fn into_incoming(self) -> Incoming { + Incoming { + inner: self.err.into_incoming(), + ep: self.ep, + } + } +} + /// Adaptor to let [`Incoming`] be `await`ed like a [`Connecting`]. #[derive(derive_more::Debug)] #[debug("IncomingFuture")] @@ -521,7 +541,7 @@ impl Accepting { /// /// See also documentation for [`Connecting::into_0rtt`]. /// - /// [`RecvStream::is_0rtt`]: quinn::RecvStream::is_0rtt + /// [`RecvStream::is_0rtt`]: crate::endpoint::RecvStream::is_0rtt pub fn into_0rtt(self) -> IncomingZeroRttConnection { let (quinn_conn, zrtt_accepted) = self .inner @@ -725,8 +745,8 @@ impl Connection { /// without writing anything to [`SendStream`] will never succeed. /// /// [`open_bi`]: Connection::open_bi - /// [`SendStream`]: quinn::SendStream - /// [`RecvStream`]: quinn::RecvStream + /// [`SendStream`]: crate::endpoint::SendStream + /// [`RecvStream`]: crate::endpoint::RecvStream #[inline] pub fn open_bi(&self) -> OpenBi<'_> { self.inner.open_bi() @@ -746,8 +766,8 @@ impl Connection { /// writing anything to the connected [`SendStream`] will never succeed. /// /// [`open_bi`]: Connection::open_bi - /// [`SendStream`]: quinn::SendStream - /// [`RecvStream`]: quinn::RecvStream + /// [`SendStream`]: crate::endpoint::SendStream + /// [`RecvStream`]: crate::endpoint::RecvStream #[inline] pub fn accept_bi(&self) -> AcceptBi<'_> { self.inner.accept_bi() @@ -876,10 +896,7 @@ impl Connection { /// Current state of the congestion control algorithm, for debugging purposes. #[inline] - pub fn congestion_state( - &self, - path_id: PathId, - ) -> Option> { + pub fn congestion_state(&self, path_id: PathId) -> Option> { self.inner.congestion_state(path_id) } @@ -931,7 +948,7 @@ impl Connection { output: &mut [u8], label: &[u8], context: &[u8], - ) -> Result<(), quinn_proto::crypto::ExportKeyingMaterialError> { + ) -> Result<(), ExportKeyingMaterialError> { self.inner.export_keying_material(output, label, context) } diff --git a/iroh/src/endpoint/hooks.rs b/iroh/src/endpoint/hooks.rs index a60dc5860c5..f0beed79443 100644 --- a/iroh/src/endpoint/hooks.rs +++ b/iroh/src/endpoint/hooks.rs @@ -1,9 +1,8 @@ use std::pin::Pin; use iroh_base::EndpointAddr; -use quinn::VarInt; -use crate::endpoint::connection::ConnectionInfo; +use crate::endpoint::{connection::ConnectionInfo, quic::VarInt}; type BoxFuture<'a, T> = Pin + Send + 'a>>; diff --git a/iroh/src/endpoint/quic.rs b/iroh/src/endpoint/quic.rs index 514e199b265..15f08cfcd36 100644 --- a/iroh/src/endpoint/quic.rs +++ b/iroh/src/endpoint/quic.rs @@ -8,16 +8,12 @@ #[cfg(feature = "qlog")] use std::path::Path; - use std::{ net::{SocketAddrV4, SocketAddrV6}, sync::Arc, time::Duration, }; -#[cfg(feature = "qlog")] -pub use quinn::{QlogConfig, QlogFactory, QlogFileFactory}; - /// `quinn` types that are used in the public iroh API. // Each type is notated with the iroh type or quinn type that uses it. pub use quinn::{ @@ -50,6 +46,8 @@ pub use quinn::{ WriteError, // quinn::SendStream Written, // quinn::SendStream }; +#[cfg(feature = "qlog")] +pub use quinn::{QlogConfig, QlogFactory, QlogFileFactory}; /// `quinn_proto` types that are used in the public iroh API. // Each type is notated with the iroh type or quinn type that uses it. pub use quinn_proto::{ @@ -587,7 +585,7 @@ impl ServerConfig { Self { inner, transport } } - /// Set a custom [`TransportConfig`] + /// Set a custom [`QuicTransportConfig`] pub fn transport_config(&mut self, transport: QuicTransportConfig) -> &mut Self { self.inner.transport_config(transport.to_arc()); self.transport = transport; @@ -642,9 +640,9 @@ impl ServerConfig { self } - /// Maximum number of [`Incoming`][crate::Incoming] to allow to exist at a time + /// Maximum number of [`Incoming`] to allow to exist at a time /// - /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt + /// An [`Incoming`] comes into existence when an incoming connection attempt /// is received and stops existing when the application either accepts it or otherwise disposes /// of it. While this limit is reached, new incoming connection attempts are immediately /// refused. Larger values have greater worst-case memory consumption, but accommodate greater @@ -653,14 +651,16 @@ impl ServerConfig { /// The default value is set to 65536. With a typical Ethernet MTU of 1500 bytes, this limits /// memory consumption from this to under 100 MiB--a generous amount that still prevents memory /// exhaustion in most contexts. + /// + /// [`Incoming`]: crate::endpoint::Incoming pub fn max_incoming(&mut self, max_incoming: usize) -> &mut Self { self.inner.max_incoming(max_incoming); self } - /// Maximum number of received bytes to buffer for each [`Incoming`][crate::Incoming] + /// Maximum number of received bytes to buffer for each [`Incoming`] /// - /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt + /// An [`Incoming`] comes into existence when an incoming connection attempt /// is received and stops existing when the application either accepts it or otherwise disposes /// of it. This limit governs only packets received within that period, and does not include /// the first packet. Packets received in excess of this limit are dropped, which may cause @@ -668,16 +668,18 @@ impl ServerConfig { /// /// The default value is set to 10 MiB--an amount such that in most situations a client would /// not transmit that much 0-RTT data faster than the server handles the corresponding - /// [`Incoming`][crate::Incoming]. + /// [`Incoming`]. + /// + /// [`Incoming`]: crate::endpoint::Incoming pub fn incoming_buffer_size(&mut self, incoming_buffer_size: u64) -> &mut Self { self.inner.incoming_buffer_size(incoming_buffer_size); self } - /// Maximum number of received bytes to buffer for all [`Incoming`][crate::Incoming] + /// Maximum number of received bytes to buffer for all [`Incoming`] /// collectively /// - /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt + /// An [`Incoming`] comes into existence when an incoming connection attempt /// is received and stops existing when the application either accepts it or otherwise disposes /// of it. This limit governs only packets received within that period, and does not include /// the first packet. Packets received in excess of this limit are dropped, which may cause @@ -685,6 +687,8 @@ impl ServerConfig { /// /// The default value is set to 100 MiB--a generous amount that still prevents memory /// exhaustion in most contexts. + /// + /// [`Incoming`]: crate::endpoint::Incoming pub fn incoming_buffer_size_total(&mut self, incoming_buffer_size_total: u64) -> &mut Self { self.inner .incoming_buffer_size_total(incoming_buffer_size_total); @@ -695,16 +699,18 @@ impl ServerConfig { /// /// This exists to allow system time to be mocked in tests, or wherever else desired. /// - /// Defaults to [`StdSystemTime`], which simply calls [`SystemTime::now()`](SystemTime::now). + /// Defaults to [`quinn::StdSystemTime`], which simply calls [`SystemTime::now()`](std::time::SystemTime::now). + /// + /// [`SystemTime`]: std::time::SystemTime pub fn time_source(&mut self, time_source: Arc) -> &mut Self { self.inner.time_source(time_source); self } - /// Create a server config with the given [`crypto::ServerConfig`] + /// Create a server config with the given [`CryptoServerConfig`] /// /// Uses a randomized handshake token key. - pub fn with_crypto(crypto: Arc) -> Self { + pub fn with_crypto(crypto: Arc) -> Self { let mut inner = quinn::ServerConfig::with_crypto(crypto); let transport = QuicTransportConfig::default(); inner.transport_config(transport.to_arc()); diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index 913a5ea29fb..7e2ce35233b 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -37,7 +37,7 @@ use n0_watcher::{self, Watchable, Watcher}; #[cfg(not(wasm_browser))] use netwatch::ip::LocalAddresses; use netwatch::netmon; -use quinn::{ServerConfig, WeakConnectionHandle}; +use quinn::WeakConnectionHandle; use rand::Rng; use tokio::sync::{Mutex as AsyncMutex, mpsc, oneshot}; use tokio_util::sync::CancellationToken; @@ -130,7 +130,7 @@ pub(crate) struct Options { pub(crate) proxy_url: Option, /// ServerConfig for the internal QUIC endpoint - pub(crate) server_config: ServerConfig, + pub(crate) server_config: quinn::ServerConfig, /// Skip verification of SSL certificates from relay servers /// @@ -1542,7 +1542,6 @@ mod tests { use n0_future::{MergeBounded, StreamExt, time}; use n0_tracing_test::traced_test; use n0_watcher::Watcher; - use quinn::ServerConfig; use rand::{CryptoRng, Rng, RngCore, SeedableRng}; use tokio_util::task::AbortOnDropHandle; use tracing::{Instrument, error, info, info_span, instrument}; @@ -1552,6 +1551,7 @@ mod tests { Endpoint, RelayMode, SecretKey, discovery::static_provider::StaticProvider, dns::DnsResolver, + endpoint::QuicTransportConfig, magicsock::{ Handle, MagicSock, TransportConfig, mapped_addrs::{EndpointIdMappedAddr, MappedAddr}, @@ -1583,12 +1583,13 @@ mod tests { } /// Generate a server config with no ALPNS and a default transport configuration - fn make_default_server_config(secret_key: &SecretKey) -> ServerConfig { + fn make_default_server_config(secret_key: &SecretKey) -> quinn::ServerConfig { let quic_server_config = crate::tls::TlsConfig::new(secret_key.clone(), DEFAULT_MAX_TLS_TICKETS) .make_server_config(vec![], false); - let mut server_config = ServerConfig::with_crypto(Arc::new(quic_server_config)); - server_config.transport_config(Arc::new(quinn::TransportConfig::default())); + let mut server_config = quinn::ServerConfig::with_crypto(Arc::new(quic_server_config)); + let transport = QuicTransportConfig::default(); + server_config.transport_config(transport.to_arc()); server_config } @@ -1953,7 +1954,7 @@ mod tests { async fn magicsock_ep(secret_key: SecretKey) -> Result { let quic_server_config = tls::TlsConfig::new(secret_key.clone(), DEFAULT_MAX_TLS_TICKETS) .make_server_config(vec![ALPN.to_vec()], true); - let mut server_config = ServerConfig::with_crypto(Arc::new(quic_server_config)); + let mut server_config = quinn::ServerConfig::with_crypto(Arc::new(quic_server_config)); server_config.transport_config(Arc::new(quinn::TransportConfig::default())); let dns_resolver = DnsResolver::new(); diff --git a/iroh/src/magicsock/remote_map/remote_state.rs b/iroh/src/magicsock/remote_map/remote_state.rs index b8d8548cac4..a1adc3aaec4 100644 --- a/iroh/src/magicsock/remote_map/remote_state.rs +++ b/iroh/src/magicsock/remote_map/remote_state.rs @@ -15,7 +15,7 @@ use n0_future::{ time::{self, Duration, Instant}, }; use n0_watcher::{Watchable, Watcher}; -use quinn::{PathStats, WeakConnectionHandle}; +use quinn::WeakConnectionHandle; use quinn_proto::{PathError, PathEvent, PathId, PathStatus, iroh_hp}; use rustc_hash::FxHashMap; use smallvec::SmallVec; @@ -33,7 +33,7 @@ use self::{ use super::Source; use crate::{ discovery::{ConcurrentDiscovery, Discovery, DiscoveryError, DiscoveryItem}, - endpoint::DirectAddr, + endpoint::{DirectAddr, quic::PathStats}, magicsock::{ MagicsockMetrics, mapped_addrs::{AddrMap, MappedAddr, RelayMappedAddr}, diff --git a/iroh/src/protocol.rs b/iroh/src/protocol.rs index 8dcd5688bad..10e52a5f6e6 100644 --- a/iroh/src/protocol.rs +++ b/iroh/src/protocol.rs @@ -53,7 +53,7 @@ use tracing::{Instrument, error, field::Empty, info_span, trace, warn}; use crate::{ Endpoint, - endpoint::{Accepting, Connection, RemoteEndpointIdError}, + endpoint::{Accepting, Connection, RemoteEndpointIdError, quic}, }; /// The built router. @@ -146,8 +146,8 @@ impl From for AcceptError { } } -impl From for AcceptError { - fn from(err: quinn::ClosedStream) -> Self { +impl From for AcceptError { + fn from(err: quic::ClosedStream) -> Self { Self::from_err(err) } } @@ -607,14 +607,13 @@ mod tests { use std::{sync::Mutex, time::Duration}; use n0_error::{Result, StdResultExt}; - use quinn::ApplicationClose; use super::*; use crate::{ RelayMode, endpoint::{ - BeforeConnectOutcome, ConnectError, ConnectWithOptsError, ConnectionError, - EndpointHooks, + ApplicationClose, BeforeConnectOutcome, ConnectError, ConnectWithOptsError, + ConnectionError, EndpointHooks, }, }; From e9b05af389e6f3d52a198eb723a04c2d9fc4885d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cramfox=E2=80=9D?= <“kasey@n0.computer”> Date: Thu, 11 Dec 2025 22:24:13 -0500 Subject: [PATCH 4/9] feat: allow creating a new `ServerConfig` from the internal crypto of the `Endpoint` --- iroh/src/endpoint.rs | 27 ++++-- iroh/src/endpoint/connection.rs | 13 ++- iroh/src/endpoint/quic.rs | 145 +++++++++++++------------------- iroh/src/magicsock.rs | 2 +- 4 files changed, 89 insertions(+), 98 deletions(-) diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index dd6b52ca239..0cffbb8a7e6 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -172,7 +172,9 @@ impl Builder { tls_config: tls::TlsConfig::new(secret_key.clone(), self.max_tls_tickets), keylog: self.keylog, }; - let server_config = static_config.create_server_config(self.alpn_protocols); + let server_config = static_config + .create_server_config(self.alpn_protocols) + .into_inner(); #[cfg(not(wasm_browser))] let dns_resolver = self.dns_resolver.unwrap_or_default(); @@ -471,15 +473,15 @@ struct StaticConfig { } impl StaticConfig { - /// Create a [`quinn::ServerConfig`] with the specified ALPN protocols. - fn create_server_config(&self, alpn_protocols: Vec>) -> quinn::ServerConfig { + /// Create a [`ServerConfig`] with the specified ALPN protocols. + fn create_server_config(&self, alpn_protocols: Vec>) -> ServerConfig { let quic_server_config = self .tls_config .make_server_config(alpn_protocols, self.keylog); - let mut server_config = quinn::ServerConfig::with_crypto(Arc::new(quic_server_config)); - server_config.transport_config(self.transport_config.to_arc()); + let mut inner = quinn::ServerConfig::with_crypto(Arc::new(quic_server_config)); + inner.transport_config(self.transport_config.to_inner_arc()); - server_config + ServerConfig::new(inner, self.transport_config.clone()) } } @@ -602,7 +604,9 @@ impl Endpoint { /// Note that this *overrides* the current list of ALPNs. pub fn set_alpns(&self, alpns: Vec>) { let server_config = self.static_config.create_server_config(alpns); - self.msock.endpoint().set_server_config(Some(server_config)); + self.msock + .endpoint() + .set_server_config(Some(server_config.into_inner())); } /// Adds the provided configuration to the [`RelayMap`]. @@ -718,8 +722,8 @@ impl Endpoint { let transport_config = options .transport_config - .map(|cfg| cfg.to_arc()) - .unwrap_or(self.static_config.transport_config.to_arc()); + .map(|cfg| cfg.to_inner_arc()) + .unwrap_or(self.static_config.transport_config.to_inner_arc()); // Start connecting via quinn. This will time out after 10 seconds if no reachable // address is available. @@ -1147,6 +1151,11 @@ impl Endpoint { self.msock.is_closed() } + /// Create a [`ServerConfig`] for this endpoint that includes the given alpns. + pub fn create_server_config(&self, alpns: Vec>) -> ServerConfig { + self.static_config.create_server_config(alpns) + } + // # Remaining private methods #[cfg(test)] diff --git a/iroh/src/endpoint/connection.rs b/iroh/src/endpoint/connection.rs index 589bfd2afed..af76d28bc68 100644 --- a/iroh/src/endpoint/connection.rs +++ b/iroh/src/endpoint/connection.rs @@ -21,6 +21,7 @@ use std::{ future::{Future, IntoFuture}, net::{IpAddr, SocketAddr}, pin::Pin, + sync::Arc, task::Poll, }; @@ -102,12 +103,20 @@ impl Incoming { /// Accepts this incoming connection using a custom configuration. /// + /// Use the [`Endpoint::create_server_config`] method to create a [`ServerConfig`] + /// that can be customized. + /// /// See [`accept()`] for more details. /// /// [`accept()`]: Incoming::accept - pub fn accept_with(self, server_config: ServerConfig) -> Result { + /// [`Endpoint::create_server_config`]: crate::Endpoint::create_server_config + /// [`ServerConfig`]: crate::endpoint::ServerConfig + pub fn accept_with( + self, + server_config: Arc, + ) -> Result { self.inner - .accept_with(server_config.to_arc()) + .accept_with(server_config.to_inner_arc()) .map(|conn| Accepting::new(conn, self.ep)) } diff --git a/iroh/src/endpoint/quic.rs b/iroh/src/endpoint/quic.rs index 15f08cfcd36..99122b4bdc3 100644 --- a/iroh/src/endpoint/quic.rs +++ b/iroh/src/endpoint/quic.rs @@ -8,11 +8,7 @@ #[cfg(feature = "qlog")] use std::path::Path; -use std::{ - net::{SocketAddrV4, SocketAddrV6}, - sync::Arc, - time::Duration, -}; +use std::{sync::Arc, time::Duration}; /// `quinn` types that are used in the public iroh API. // Each type is notated with the iroh type or quinn type that uses it. @@ -129,12 +125,12 @@ impl Default for QuicTransportConfig { } impl QuicTransportConfig { - /// Return an `Arc`-d [`quinn::TransportConfig`] - pub(crate) fn to_arc(&self) -> Arc { + /// Return an `Arc`-d [`quinn::TransportConfig`]. + pub(crate) fn to_inner_arc(&self) -> Arc { Arc::new(self.0.clone()) } - /// Maximum number of incoming bidirectional streams that may be open concurrently + /// Maximum number of incoming bidirectional streams that may be open concurrently. /// /// Must be nonzero for the peer to open any bidirectional streams. /// @@ -145,7 +141,7 @@ impl QuicTransportConfig { self } - /// Variant of `max_concurrent_bidi_streams` affecting unidirectional streams + /// Variant of `max_concurrent_bidi_streams` affecting unidirectional streams. pub fn max_concurrent_uni_streams(&mut self, value: VarInt) -> &mut Self { self.0.max_concurrent_uni_streams(value); self @@ -202,7 +198,7 @@ impl QuicTransportConfig { self } - /// Maximum number of bytes to transmit to a peer without acknowledgment + /// Maximum number of bytes to transmit to a peer without acknowledgment. /// /// Provides an upper bound on memory when communicating with peers that issue large amounts of /// flow control credit. Endpoints that wish to handle large numbers of connections robustly @@ -236,13 +232,13 @@ impl QuicTransportConfig { } /// Maximum reordering in time space before time based loss detection considers a packet lost, - /// as a factor of RTT + /// as a factor of RTT. pub fn time_threshold(&mut self, value: f32) -> &mut Self { self.0.time_threshold(value); self } - /// The RTT used before an RTT sample is taken + /// The RTT used before an RTT sample is taken. pub fn initial_rtt(&mut self, value: Duration) -> &mut Self { self.0.initial_rtt(value); self @@ -287,7 +283,7 @@ impl QuicTransportConfig { self } - /// Pad UDP datagrams carrying application data to current maximum UDP payload size + /// Pad UDP datagrams carrying application data to current maximum UDP payload size. /// /// Disabled by default. UDP datagrams containing loss probes are exempt from padding. /// @@ -301,7 +297,7 @@ impl QuicTransportConfig { self } - /// Specifies the ACK frequency config (see [`AckFrequencyConfig`] for details) + /// Specifies the ACK frequency config (see [`AckFrequencyConfig`] for details). /// /// The provided configuration will be ignored if the peer does not support the acknowledgement /// frequency QUIC extension. @@ -320,7 +316,7 @@ impl QuicTransportConfig { self } - /// Period of inactivity before sending a keep-alive packet + /// Period of inactivity before sending a keep-alive packet. /// /// Keep-alive packets prevent an inactive but otherwise healthy connection from timing out. /// @@ -332,13 +328,13 @@ impl QuicTransportConfig { self } - /// Maximum quantity of out-of-order crypto layer data to buffer + /// Maximum quantity of out-of-order crypto layer data to buffer. pub fn crypto_buffer_size(&mut self, value: usize) -> &mut Self { self.0.crypto_buffer_size(value); self } - /// Whether the implementation is permitted to set the spin bit on this connection + /// Whether the implementation is permitted to set the spin bit on this connection. /// /// This allows passive observers to easily judge the round trip time of a connection, which can /// be useful for network administration but sacrifices a small amount of privacy. @@ -348,7 +344,7 @@ impl QuicTransportConfig { } /// Maximum number of incoming application datagram bytes to buffer, or None to disable - /// incoming datagrams + /// incoming datagrams. /// /// The peer is forbidden to send single datagrams larger than this size. If the aggregate size /// of all datagrams that have been received from the peer but not consumed by the application @@ -358,7 +354,7 @@ impl QuicTransportConfig { self } - /// Maximum number of outgoing application datagram bytes to buffer + /// Maximum number of outgoing application datagram bytes to buffer. /// /// While datagrams are sent ASAP, it is possible for an application to generate data faster /// than the link, or even the underlying hardware, can transmit them. This limits the amount of @@ -369,7 +365,7 @@ impl QuicTransportConfig { self } - /// How to construct new `congestion::Controller`s + /// How to construct new `congestion::Controller`s. /// /// Typically the refcounted configuration of a `congestion::Controller`, /// e.g. a `congestion::NewRenoConfig`. @@ -389,7 +385,7 @@ impl QuicTransportConfig { } /// Whether to use "Generic Segmentation Offload" to accelerate transmits, when supported by the - /// environment + /// environment. /// /// Defaults to `true`. /// @@ -445,7 +441,7 @@ impl QuicTransportConfig { self } - /// Sets a default per-path maximum idle timeout + /// Sets a default per-path maximum idle timeout. /// /// If the path is idle for this long the path will be abandoned. Bear in mind this will /// interact with the [`QuicTransportConfig::max_idle_timeout`], if the last path is @@ -464,7 +460,7 @@ impl QuicTransportConfig { self } - /// Sets a default per-path keep alive interval + /// Sets a default per-path keep alive interval. /// /// Note that this does not interact with the connection-wide /// [`QuicTransportConfig::keep_alive_interval`]. This setting will keep this path active, @@ -485,7 +481,7 @@ impl QuicTransportConfig { } /// Sets the maximum number of nat traversal addresses this endpoint allows the remote to - /// advertise + /// advertise. /// /// Setting this to any nonzero value will enable Iroh's holepunching, loosely based in the Nat /// Traversal Extension for QUIC, see @@ -544,9 +540,14 @@ impl QuicTransportConfig { } } -/// Parameters governing incoming connections +/// Parameters governing incoming connections. /// /// Default values should be suitable for most internet applications. +/// +/// To create a [`ServerConfig`] compatible with your [`Endpoint`] identity, use the [`Endpoint::create_server_config`] method. +/// +/// [`Endpoint`]: crate::Endpoint +/// [`Endpoint::create_server_config`]: crate::Endpoint::create_server_config // Note: used in `iroh::endpoint::connection::Incoming::accept_with` // This is new-typed since `quinn::ServerConfig` takes a `TransportConfig`, which we new-type as a `QuicTransportConfig` #[derive(Debug, Clone)] @@ -556,44 +557,42 @@ pub struct ServerConfig { } impl ServerConfig { - /// Transport configuration to use for incoming connections - pub fn transport(&self) -> QuicTransportConfig { + pub(crate) fn new(inner: quinn::ServerConfig, transport: QuicTransportConfig) -> Self { + Self { inner, transport } + } + + pub(crate) fn to_inner_arc(&self) -> Arc { + Arc::new(self.inner.clone()) + } + + pub(crate) fn into_inner(self) -> quinn::ServerConfig { + self.inner + } + + /// Transport configuration used for incoming connections. + pub fn transport_config(&self) -> QuicTransportConfig { self.transport.clone() } - /// TLS configuration used for incoming connections - /// - /// Must be set to use TLS 1.3 only. + /// TLS configuration used for incoming connections. pub fn crypto(&self) -> Arc { self.inner.crypto.clone() } - /// Configuration for sending and handling validation tokens + /// Configuration for sending and handling validation tokens. pub fn validation_token(&self) -> ValidationTokenConfig { self.inner.validation_token.clone() } - pub(crate) fn to_arc(&self) -> Arc { - Arc::new(self.inner.clone()) - } - - /// Create a default config with a particular handshake token key - pub fn new(crypto: Arc, token_key: Arc) -> Self { - let mut inner = quinn::ServerConfig::new(crypto, token_key); - let transport = QuicTransportConfig::default(); - inner.transport_config(transport.to_arc()); - Self { inner, transport } - } - - /// Set a custom [`QuicTransportConfig`] - pub fn transport_config(&mut self, transport: QuicTransportConfig) -> &mut Self { - self.inner.transport_config(transport.to_arc()); + /// Sets a custom [`QuicTransportConfig`]. + pub fn set_transport_config(&mut self, transport: QuicTransportConfig) -> &mut Self { + self.inner.transport_config(transport.to_inner_arc()); self.transport = transport; self } - /// Set a custom [`ValidationTokenConfig`] - pub fn validation_token_config( + /// Sets a custom [`ValidationTokenConfig`]. + pub fn set_validation_token_config( &mut self, validation_token: ValidationTokenConfig, ) -> &mut Self { @@ -602,7 +601,7 @@ impl ServerConfig { } /// Private key used to authenticate data included in handshake tokens - pub fn token_key(&mut self, value: Arc) -> &mut Self { + pub fn set_token_key(&mut self, value: Arc) -> &mut Self { self.inner.token_key(value); self } @@ -610,37 +609,21 @@ impl ServerConfig { /// Duration after a retry token was issued for which it's considered valid /// /// Defaults to 15 seconds. - pub fn retry_token_lifetime(&mut self, value: Duration) -> &mut Self { + pub fn set_retry_token_lifetime(&mut self, value: Duration) -> &mut Self { self.inner.retry_token_lifetime(value); self } - /// Whether to allow clients to migrate to new addresses + /// Whether to allow clients to migrate to new addresses. /// /// Improves behavior for clients that move between different internet connections or suffer NAT /// rebinding. Enabled by default. - pub fn migration(&mut self, value: bool) -> &mut Self { + pub fn set_migration(&mut self, value: bool) -> &mut Self { self.inner.migration(value); self } - /// The preferred IPv4 address that will be communicated to clients during handshaking - /// - /// If the client is able to reach this address, it will switch to it. - pub fn preferred_address_v4(&mut self, address: Option) -> &mut Self { - self.inner.preferred_address_v4(address); - self - } - - /// The preferred IPv6 address that will be communicated to clients during handshaking - /// - /// If the client is able to reach this address, it will switch to it. - pub fn preferred_address_v6(&mut self, address: Option) -> &mut Self { - self.inner.preferred_address_v6(address); - self - } - - /// Maximum number of [`Incoming`] to allow to exist at a time + /// Maximum number of [`Incoming`] to allow to exist at a time. /// /// An [`Incoming`] comes into existence when an incoming connection attempt /// is received and stops existing when the application either accepts it or otherwise disposes @@ -653,12 +636,12 @@ impl ServerConfig { /// exhaustion in most contexts. /// /// [`Incoming`]: crate::endpoint::Incoming - pub fn max_incoming(&mut self, max_incoming: usize) -> &mut Self { + pub fn set_max_incoming(&mut self, max_incoming: usize) -> &mut Self { self.inner.max_incoming(max_incoming); self } - /// Maximum number of received bytes to buffer for each [`Incoming`] + /// Maximum number of received bytes to buffer for each [`Incoming`]. /// /// An [`Incoming`] comes into existence when an incoming connection attempt /// is received and stops existing when the application either accepts it or otherwise disposes @@ -671,13 +654,13 @@ impl ServerConfig { /// [`Incoming`]. /// /// [`Incoming`]: crate::endpoint::Incoming - pub fn incoming_buffer_size(&mut self, incoming_buffer_size: u64) -> &mut Self { + pub fn set_incoming_buffer_size(&mut self, incoming_buffer_size: u64) -> &mut Self { self.inner.incoming_buffer_size(incoming_buffer_size); self } /// Maximum number of received bytes to buffer for all [`Incoming`] - /// collectively + /// collectively. /// /// An [`Incoming`] comes into existence when an incoming connection attempt /// is received and stops existing when the application either accepts it or otherwise disposes @@ -689,31 +672,21 @@ impl ServerConfig { /// exhaustion in most contexts. /// /// [`Incoming`]: crate::endpoint::Incoming - pub fn incoming_buffer_size_total(&mut self, incoming_buffer_size_total: u64) -> &mut Self { + pub fn set_incoming_buffer_size_total(&mut self, incoming_buffer_size_total: u64) -> &mut Self { self.inner .incoming_buffer_size_total(incoming_buffer_size_total); self } - /// Object to get current [`SystemTime`] + /// Object to get current [`SystemTime`]. /// /// This exists to allow system time to be mocked in tests, or wherever else desired. /// /// Defaults to [`quinn::StdSystemTime`], which simply calls [`SystemTime::now()`](std::time::SystemTime::now). /// /// [`SystemTime`]: std::time::SystemTime - pub fn time_source(&mut self, time_source: Arc) -> &mut Self { + pub fn set_time_source(&mut self, time_source: Arc) -> &mut Self { self.inner.time_source(time_source); self } - - /// Create a server config with the given [`CryptoServerConfig`] - /// - /// Uses a randomized handshake token key. - pub fn with_crypto(crypto: Arc) -> Self { - let mut inner = quinn::ServerConfig::with_crypto(crypto); - let transport = QuicTransportConfig::default(); - inner.transport_config(transport.to_arc()); - Self { inner, transport } - } } diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index 7e2ce35233b..6eea6d67116 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -1589,7 +1589,7 @@ mod tests { .make_server_config(vec![], false); let mut server_config = quinn::ServerConfig::with_crypto(Arc::new(quic_server_config)); let transport = QuicTransportConfig::default(); - server_config.transport_config(transport.to_arc()); + server_config.transport_config(transport.to_inner_arc()); server_config } From f7bc6ba87dcc89921d7059bee4adbec8c22cf945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cramfox=E2=80=9D?= <“kasey@n0.computer”> Date: Fri, 12 Dec 2025 17:30:54 -0500 Subject: [PATCH 5/9] refactor: create builder structs for `ServerConfig` and `QuicTransportConfig`, to enable arc-ing the inner quinn structs --- iroh/bench/src/iroh.rs | 17 +++--- iroh/examples/transfer.rs | 6 +- iroh/src/discovery.rs | 10 ++-- iroh/src/endpoint.rs | 35 ++++++------ iroh/src/endpoint/connection.rs | 7 ++- iroh/src/endpoint/quic.rs | 97 +++++++++++++++++++++------------ iroh/src/magicsock.rs | 2 +- iroh/src/test_utils/qlog.rs | 10 ++-- 8 files changed, 108 insertions(+), 76 deletions(-) diff --git a/iroh/bench/src/iroh.rs b/iroh/bench/src/iroh.rs index 35ffb10d556..e40fff433d1 100644 --- a/iroh/bench/src/iroh.rs +++ b/iroh/bench/src/iroh.rs @@ -6,7 +6,10 @@ use std::{ use bytes::Bytes; use iroh::{ Endpoint, EndpointAddr, RelayMode, RelayUrl, - endpoint::{Connection, ConnectionError, QuicTransportConfig, RecvStream, SendStream}, + endpoint::{ + Connection, ConnectionError, QuicTransportConfig, QuicTransportConfigBuilder, RecvStream, + SendStream, + }, }; use n0_error::{Result, StackResultExt, StdResultExt}; use tracing::{trace, warn}; @@ -125,18 +128,18 @@ pub async fn connect_client( pub fn transport_config(max_streams: usize, initial_mtu: u16) -> QuicTransportConfig { // High stream windows are chosen because the amount of concurrent streams // is configurable as a parameter. - let mut config = QuicTransportConfig::default(); - config.max_concurrent_uni_streams(max_streams.try_into().unwrap()); - config.initial_mtu(initial_mtu); + let mut builder = QuicTransportConfigBuilder::default(); + builder.max_concurrent_uni_streams(max_streams.try_into().unwrap()); + builder.initial_mtu(initial_mtu); let mut acks = quinn::AckFrequencyConfig::default(); acks.ack_eliciting_threshold(10u32.into()); - config.ack_frequency_config(Some(acks)); + builder.ack_frequency_config(Some(acks)); #[cfg(feature = "qlog")] - config.qlog_from_env("bench-iroh"); + builder.qlog_from_env("bench-iroh"); - config + builder.build() } async fn drain_stream( diff --git a/iroh/examples/transfer.rs b/iroh/examples/transfer.rs index dcb336db5c9..71346b0e846 100644 --- a/iroh/examples/transfer.rs +++ b/iroh/examples/transfer.rs @@ -268,9 +268,9 @@ impl EndpointArgs { #[cfg(feature = "qlog")] { - let mut transport_config = iroh::endpoint::QuicTransportConfig::default(); - transport_config.qlog_from_env("transfer"); - builder = builder.transport_config(transport_config) + let mut cfg_builder = iroh::endpoint::QuicTransportConfigBuilder::default(); + cfg_builder.qlog_from_env("transfer"); + builder = builder.transport_config(cfg_builder.build()) } let endpoint = builder.alpns(vec![TRANSFER_ALPN.to_vec()]).bind().await?; diff --git a/iroh/src/discovery.rs b/iroh/src/discovery.rs index aab0b663088..cfa09a550d8 100644 --- a/iroh/src/discovery.rs +++ b/iroh/src/discovery.rs @@ -509,7 +509,7 @@ mod tests { use super::*; use crate::{ Endpoint, RelayMode, - endpoint::{ConnectOptions, IdleTimeout, QuicTransportConfig}, + endpoint::{ConnectOptions, IdleTimeout, QuicTransportConfigBuilder}, }; type InfoStore = HashMap; @@ -727,10 +727,10 @@ mod tests { .await; // 10x faster test via a 3s idle timeout instead of the 30s default - let mut config = QuicTransportConfig::default(); - config.keep_alive_interval(Duration::from_secs(1)); - config.max_idle_timeout(Some(IdleTimeout::try_from(Duration::from_secs(3)).unwrap())); - let opts = ConnectOptions::new().with_transport_config(config); + let mut builder = QuicTransportConfigBuilder::default(); + builder.keep_alive_interval(Duration::from_secs(1)); + builder.max_idle_timeout(Some(IdleTimeout::try_from(Duration::from_secs(3)).unwrap())); + let opts = ConnectOptions::new().with_transport_config(builder.build()); let res = ep2 .connect_with_opts(ep1.id(), TEST_ALPN, opts) diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index 0cffbb8a7e6..a71af1b5f0a 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -63,12 +63,12 @@ pub use self::{ ControllerFactory, ControllerMetrics, CryptoError, CryptoServerConfig, Dir, ExportKeyingMaterialError, FrameStats, FrameType, HandshakeTokenKey, HeaderKey, IdleTimeout, Keys, MtuDiscoveryConfig, OpenBi, OpenUni, PacketKey, PathId, PathStats, - QuicConnectError, QuicTransportConfig, ReadDatagram, ReadError, ReadExactError, - ReadToEndError, RecvStream, ResetError, RttEstimator, SendDatagram, SendDatagramError, - SendStream, ServerConfig, Session, Side, StoppedError, StreamId, TimeSource, TokenLog, - TokenReuseError, TransportError, TransportErrorCode, TransportParameters, UdpStats, - UnsupportedVersion, ValidationTokenConfig, VarInt, VarIntBoundsExceeded, WriteError, - Written, + QuicConnectError, QuicTransportConfig, QuicTransportConfigBuilder, ReadDatagram, ReadError, + ReadExactError, ReadToEndError, RecvStream, ResetError, RttEstimator, SendDatagram, + SendDatagramError, SendStream, ServerConfig, ServerConfigBuilder, Session, Side, + StoppedError, StreamId, TimeSource, TokenLog, TokenReuseError, TransportError, + TransportErrorCode, TransportParameters, UdpStats, UnsupportedVersion, + ValidationTokenConfig, VarInt, VarIntBoundsExceeded, WriteError, Written, }, }; pub use crate::magicsock::transports::TransportConfig; @@ -172,9 +172,7 @@ impl Builder { tls_config: tls::TlsConfig::new(secret_key.clone(), self.max_tls_tickets), keylog: self.keylog, }; - let server_config = static_config - .create_server_config(self.alpn_protocols) - .into_inner(); + let server_config = static_config.create_server_config(self.alpn_protocols); #[cfg(not(wasm_browser))] let dns_resolver = self.dns_resolver.unwrap_or_default(); @@ -474,14 +472,13 @@ struct StaticConfig { impl StaticConfig { /// Create a [`ServerConfig`] with the specified ALPN protocols. - fn create_server_config(&self, alpn_protocols: Vec>) -> ServerConfig { + fn create_server_config(&self, alpn_protocols: Vec>) -> quinn_proto::ServerConfig { let quic_server_config = self .tls_config .make_server_config(alpn_protocols, self.keylog); let mut inner = quinn::ServerConfig::with_crypto(Arc::new(quic_server_config)); inner.transport_config(self.transport_config.to_inner_arc()); - - ServerConfig::new(inner, self.transport_config.clone()) + inner } } @@ -604,9 +601,7 @@ impl Endpoint { /// Note that this *overrides* the current list of ALPNs. pub fn set_alpns(&self, alpns: Vec>) { let server_config = self.static_config.create_server_config(alpns); - self.msock - .endpoint() - .set_server_config(Some(server_config.into_inner())); + self.msock.endpoint().set_server_config(Some(server_config)); } /// Adds the provided configuration to the [`RelayMap`]. @@ -1151,9 +1146,13 @@ impl Endpoint { self.msock.is_closed() } - /// Create a [`ServerConfig`] for this endpoint that includes the given alpns. - pub fn create_server_config(&self, alpns: Vec>) -> ServerConfig { - self.static_config.create_server_config(alpns) + /// Create a [`ServerConfigBuilder`] for this endpoint that includes the given alpns. + /// + /// Use the [`ServerConfigBuilder`] to customize the [`ServerConfig`] connection configuration + /// for a connection accepted using the [`Incoming::accept_with`] method. + pub fn create_server_config_builder(&self, alpns: Vec>) -> ServerConfigBuilder { + let inner = self.static_config.create_server_config(alpns); + ServerConfigBuilder::new(inner, self.static_config.transport_config.clone()) } // # Remaining private methods diff --git a/iroh/src/endpoint/connection.rs b/iroh/src/endpoint/connection.rs index af76d28bc68..67d7ca26290 100644 --- a/iroh/src/endpoint/connection.rs +++ b/iroh/src/endpoint/connection.rs @@ -103,13 +103,14 @@ impl Incoming { /// Accepts this incoming connection using a custom configuration. /// - /// Use the [`Endpoint::create_server_config`] method to create a [`ServerConfig`] - /// that can be customized. + /// Use the [`Endpoint::create_server_config_builder`] method to create a [`ServerConfigBuilder`] + /// to customize a [`ServerConfig`]. /// /// See [`accept()`] for more details. /// /// [`accept()`]: Incoming::accept - /// [`Endpoint::create_server_config`]: crate::Endpoint::create_server_config + /// [`Endpoint::create_server_config_builder`]: crate::Endpoint::create_server_config_builder + /// [`ServerConfigBuilder`]: crate::endpoint::ServerConfigBuilder /// [`ServerConfig`]: crate::endpoint::ServerConfig pub fn accept_with( self, diff --git a/iroh/src/endpoint/quic.rs b/iroh/src/endpoint/quic.rs index 99122b4bdc3..2ccc7f84892 100644 --- a/iroh/src/endpoint/quic.rs +++ b/iroh/src/endpoint/quic.rs @@ -104,14 +104,14 @@ use crate::magicsock::{HEARTBEAT_INTERVAL, MAX_MULTIPATH_PATHS, PATH_MAX_IDLE_TI /// well with QUIC multipath. Adjusting those settings may cause suboptimal usage. /// /// Look at the following methods for more details: -/// - [`QuicTransportConfig::default_path_keep_alive_interval`] -/// - [`QuicTransportConfig::default_path_max_idle_timeout`] -/// - [`QuicTransportConfig::max_concurrent_multipath_paths`] -/// - [`QuicTransportConfig::set_max_remote_nat_traversal_addresses`] +/// - [`QuicTransportConfigBuilder::default_path_keep_alive_interval`] +/// - [`QuicTransportConfigBuilder::default_path_max_idle_timeout`] +/// - [`QuicTransportConfigBuilder::max_concurrent_multipath_paths`] +/// - [`QuicTransportConfigBuilder::set_max_remote_nat_traversal_addresses`] #[derive(Debug, Clone)] -pub struct QuicTransportConfig(pub(crate) quinn::TransportConfig); +pub struct QuicTransportConfigBuilder(quinn::TransportConfig); -impl Default for QuicTransportConfig { +impl Default for QuicTransportConfigBuilder { fn default() -> Self { let mut cfg = quinn::TransportConfig::default(); // Override some transport config settings. @@ -124,10 +124,26 @@ impl Default for QuicTransportConfig { } } +/// todo: docs +#[derive(Debug, Clone)] +pub struct QuicTransportConfig(Arc); + +impl Default for QuicTransportConfig { + fn default() -> Self { + QuicTransportConfigBuilder::default().build() + } +} + impl QuicTransportConfig { - /// Return an `Arc`-d [`quinn::TransportConfig`]. pub(crate) fn to_inner_arc(&self) -> Arc { - Arc::new(self.0.clone()) + self.0.clone() + } +} + +impl QuicTransportConfigBuilder { + /// Todo: docs + pub fn build(self) -> QuicTransportConfig { + QuicTransportConfig(Arc::new(self.0)) } /// Maximum number of incoming bidirectional streams that may be open concurrently. @@ -245,13 +261,13 @@ impl QuicTransportConfig { } /// The initial value to be used as the maximum UDP payload size before running MTU discovery - /// (see [`QuicTransportConfig::mtu_discovery_config`]). + /// (see [`QuicTransportConfigBuilder::mtu_discovery_config`]). /// /// Must be at least 1200, which is the default, and known to be safe for typical internet /// applications. Larger values are more efficient, but increase the risk of packet loss due to /// exceeding the network path's IP MTU. If the provided value is higher than what the network /// path actually supports, packet loss will eventually trigger black hole detection and bring - /// it down to [`QuicTransportConfig::min_mtu`]. + /// it down to [`QuicTransportConfigBuilder::min_mtu`]. pub fn initial_mtu(&mut self, value: u16) -> &mut Self { self.0.initial_mtu(value); self @@ -260,15 +276,15 @@ impl QuicTransportConfig { /// The maximum UDP payload size guaranteed to be supported by the network. /// /// Must be at least 1200, which is the default, and lower than or equal to - /// [`QuicTransportConfig::initial_mtu`]. + /// [`QuicTransportConfigBuilder::initial_mtu`]. /// /// Real-world MTUs can vary according to ISP, VPN, and properties of intermediate network links /// outside of either endpoint's control. Extreme care should be used when raising this value /// outside of private networks where these factors are fully controlled. If the provided value /// is higher than what the network path actually supports, the result will be unpredictable and /// catastrophic packet loss, without a possibility of repair. Prefer - /// [`QuicTransportConfig::initial_mtu`] together with - /// [`QuicTransportConfig::mtu_discovery_config`] to set a maximum UDP payload size that robustly + /// [`QuicTransportConfigBuilder::initial_mtu`] together with + /// [`QuicTransportConfigBuilder::mtu_discovery_config`] to set a maximum UDP payload size that robustly /// adapts to the network. pub fn min_mtu(&mut self, value: u16) -> &mut Self { self.0.min_mtu(value); @@ -444,7 +460,7 @@ impl QuicTransportConfig { /// Sets a default per-path maximum idle timeout. /// /// If the path is idle for this long the path will be abandoned. Bear in mind this will - /// interact with the [`QuicTransportConfig::max_idle_timeout`], if the last path is + /// interact with the [`QuicTransportConfigBuilder::max_idle_timeout`], if the last path is /// abandoned the entire connection will be closed. /// /// Note: this method will ignore values higher than the recommended 6500 ms and will log a warning. @@ -463,8 +479,8 @@ impl QuicTransportConfig { /// Sets a default per-path keep alive interval. /// /// Note that this does not interact with the connection-wide - /// [`QuicTransportConfig::keep_alive_interval`]. This setting will keep this path active, - /// [`QuicTransportConfig::keep_alive_interval`] will keep the connection active, with no + /// [`QuicTransportConfigBuilder::keep_alive_interval`]. This setting will keep this path active, + /// [`QuicTransportConfigBuilder::keep_alive_interval`] will keep the connection active, with no /// control over which path is used for this. /// /// Note: this method will ignore values higher than the recommended 5 seconds and will log a warning. @@ -544,29 +560,51 @@ impl QuicTransportConfig { /// /// Default values should be suitable for most internet applications. /// -/// To create a [`ServerConfig`] compatible with your [`Endpoint`] identity, use the [`Endpoint::create_server_config`] method. +/// To create a [`ServerConfig`] compatible with your [`Endpoint`] identity, use the [`Endpoint::create_server_config_builder`] method. /// /// [`Endpoint`]: crate::Endpoint -/// [`Endpoint::create_server_config`]: crate::Endpoint::create_server_config +/// [`Endpoint::create_server_config_builder`]: crate::Endpoint::create_server_config_builder // Note: used in `iroh::endpoint::connection::Incoming::accept_with` // This is new-typed since `quinn::ServerConfig` takes a `TransportConfig`, which we new-type as a `QuicTransportConfig` #[derive(Debug, Clone)] -pub struct ServerConfig { +pub struct ServerConfigBuilder { inner: quinn::ServerConfig, transport: QuicTransportConfig, } +/// todo: docs +#[derive(Debug, Clone)] +pub struct ServerConfig(Arc); + impl ServerConfig { - pub(crate) fn new(inner: quinn::ServerConfig, transport: QuicTransportConfig) -> Self { - Self { inner, transport } + pub(crate) fn to_inner_arc(&self) -> Arc { + self.0.clone() } - pub(crate) fn to_inner_arc(&self) -> Arc { - Arc::new(self.inner.clone()) + /// Transport configuration used for incoming connections. + pub fn transport_config(&self) -> QuicTransportConfig { + QuicTransportConfig(self.0.transport.clone()) } - pub(crate) fn into_inner(self) -> quinn::ServerConfig { - self.inner + /// TLS configuration used for incoming connections. + pub fn crypto(&self) -> Arc { + self.0.crypto.clone() + } + + /// Configuration for sending and handling validation tokens. + pub fn validation_token(&self) -> ValidationTokenConfig { + self.0.validation_token.clone() + } +} + +impl ServerConfigBuilder { + /// todo: docs + pub fn build(self) -> ServerConfig { + ServerConfig(Arc::new(self.inner)) + } + + pub(crate) fn new(inner: quinn::ServerConfig, transport: QuicTransportConfig) -> Self { + Self { inner, transport } } /// Transport configuration used for incoming connections. @@ -614,15 +652,6 @@ impl ServerConfig { self } - /// Whether to allow clients to migrate to new addresses. - /// - /// Improves behavior for clients that move between different internet connections or suffer NAT - /// rebinding. Enabled by default. - pub fn set_migration(&mut self, value: bool) -> &mut Self { - self.inner.migration(value); - self - } - /// Maximum number of [`Incoming`] to allow to exist at a time. /// /// An [`Incoming`] comes into existence when an incoming connection attempt diff --git a/iroh/src/magicsock.rs b/iroh/src/magicsock.rs index 6eea6d67116..abb61bd949c 100644 --- a/iroh/src/magicsock.rs +++ b/iroh/src/magicsock.rs @@ -130,7 +130,7 @@ pub(crate) struct Options { pub(crate) proxy_url: Option, /// ServerConfig for the internal QUIC endpoint - pub(crate) server_config: quinn::ServerConfig, + pub(crate) server_config: quinn_proto::ServerConfig, /// Skip verification of SSL certificates from relay servers /// diff --git a/iroh/src/test_utils/qlog.rs b/iroh/src/test_utils/qlog.rs index 17b733271e0..a477cbea323 100644 --- a/iroh/src/test_utils/qlog.rs +++ b/iroh/src/test_utils/qlog.rs @@ -8,9 +8,9 @@ use n0_error::Result; #[cfg(feature = "qlog")] use n0_future::time::Instant; -#[cfg(feature = "qlog")] -use crate::endpoint::QlogFileFactory; use crate::endpoint::QuicTransportConfig; +#[cfg(feature = "qlog")] +use crate::endpoint::{QlogFileFactory, QuicTransportConfigBuilder}; /// Builder to create one or more related qlog configs. /// @@ -72,16 +72,16 @@ impl QlogFileGroup { } #[cfg(feature = "qlog")] { - let mut config = QuicTransportConfig::default(); + let mut builder = QuicTransportConfigBuilder::default(); if std::env::var("IROH_TEST_QLOG").is_ok() { let prefix = format!("{}.{}", self.title, name.to_string()); let factory = QlogFileFactory::new(self.directory.clone()) .with_prefix(prefix) .with_start_instant(self.start.into()); - config.qlog_factory(Arc::new(factory)); + builder.qlog_factory(Arc::new(factory)); } - Ok(config) + Ok(builder.build()) } } } From 588ee4116b7171bb90b276151c35e85caf67ad51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cramfox=E2=80=9D?= <“kasey@n0.computer”> Date: Mon, 15 Dec 2025 14:20:55 -0500 Subject: [PATCH 6/9] use rust idiomatic builder pattern --- iroh/bench/src/iroh.rs | 17 ++-- iroh/examples/transfer.rs | 7 +- iroh/src/discovery.rs | 11 +-- iroh/src/endpoint/quic.rs | 174 ++++++++++++++++++------------------ iroh/src/test_utils/qlog.rs | 8 +- 5 files changed, 111 insertions(+), 106 deletions(-) diff --git a/iroh/bench/src/iroh.rs b/iroh/bench/src/iroh.rs index e40fff433d1..6f0a0f027ea 100644 --- a/iroh/bench/src/iroh.rs +++ b/iroh/bench/src/iroh.rs @@ -6,10 +6,7 @@ use std::{ use bytes::Bytes; use iroh::{ Endpoint, EndpointAddr, RelayMode, RelayUrl, - endpoint::{ - Connection, ConnectionError, QuicTransportConfig, QuicTransportConfigBuilder, RecvStream, - SendStream, - }, + endpoint::{Connection, ConnectionError, QuicTransportConfig, RecvStream, SendStream}, }; use n0_error::{Result, StackResultExt, StdResultExt}; use tracing::{trace, warn}; @@ -128,16 +125,18 @@ pub async fn connect_client( pub fn transport_config(max_streams: usize, initial_mtu: u16) -> QuicTransportConfig { // High stream windows are chosen because the amount of concurrent streams // is configurable as a parameter. - let mut builder = QuicTransportConfigBuilder::default(); - builder.max_concurrent_uni_streams(max_streams.try_into().unwrap()); - builder.initial_mtu(initial_mtu); + let mut builder = QuicTransportConfig::builder() + .max_concurrent_uni_streams(max_streams.try_into().unwrap()) + .initial_mtu(initial_mtu); let mut acks = quinn::AckFrequencyConfig::default(); acks.ack_eliciting_threshold(10u32.into()); - builder.ack_frequency_config(Some(acks)); + builder = builder.ack_frequency_config(Some(acks)); #[cfg(feature = "qlog")] - builder.qlog_from_env("bench-iroh"); + { + builder = builder.qlog_from_env("bench-iroh"); + } builder.build() } diff --git a/iroh/examples/transfer.rs b/iroh/examples/transfer.rs index 71346b0e846..6f6bb62470d 100644 --- a/iroh/examples/transfer.rs +++ b/iroh/examples/transfer.rs @@ -268,9 +268,10 @@ impl EndpointArgs { #[cfg(feature = "qlog")] { - let mut cfg_builder = iroh::endpoint::QuicTransportConfigBuilder::default(); - cfg_builder.qlog_from_env("transfer"); - builder = builder.transport_config(cfg_builder.build()) + let cfg = iroh::endpoint::QuicTransportConfig::builder() + .qlog_from_env("transfer") + .build(); + builder = builder.transport_config(cfg) } let endpoint = builder.alpns(vec![TRANSFER_ALPN.to_vec()]).bind().await?; diff --git a/iroh/src/discovery.rs b/iroh/src/discovery.rs index cfa09a550d8..1a66b1b0e46 100644 --- a/iroh/src/discovery.rs +++ b/iroh/src/discovery.rs @@ -509,7 +509,7 @@ mod tests { use super::*; use crate::{ Endpoint, RelayMode, - endpoint::{ConnectOptions, IdleTimeout, QuicTransportConfigBuilder}, + endpoint::{ConnectOptions, IdleTimeout, QuicTransportConfig}, }; type InfoStore = HashMap; @@ -727,10 +727,11 @@ mod tests { .await; // 10x faster test via a 3s idle timeout instead of the 30s default - let mut builder = QuicTransportConfigBuilder::default(); - builder.keep_alive_interval(Duration::from_secs(1)); - builder.max_idle_timeout(Some(IdleTimeout::try_from(Duration::from_secs(3)).unwrap())); - let opts = ConnectOptions::new().with_transport_config(builder.build()); + let cfg = QuicTransportConfig::builder() + .keep_alive_interval(Duration::from_secs(1)) + .max_idle_timeout(Some(IdleTimeout::try_from(Duration::from_secs(3)).unwrap())) + .build(); + let opts = ConnectOptions::new().with_transport_config(cfg); let res = ep2 .connect_with_opts(ep1.id(), TEST_ALPN, opts) diff --git a/iroh/src/endpoint/quic.rs b/iroh/src/endpoint/quic.rs index 2ccc7f84892..198d1ee0e82 100644 --- a/iroh/src/endpoint/quic.rs +++ b/iroh/src/endpoint/quic.rs @@ -87,6 +87,10 @@ use tracing::warn; use crate::magicsock::{HEARTBEAT_INTERVAL, MAX_MULTIPATH_PATHS, PATH_MAX_IDLE_TIMEOUT}; +/// Builder for a [`QuicTransportConfig`]. +#[derive(Debug, Clone)] +pub struct QuicTransportConfigBuilder(quinn::TransportConfig); + /// Parameters governing the core QUIC state machine /// /// Default values should be suitable for most internet applications. Applications protocols which @@ -100,6 +104,8 @@ use crate::magicsock::{HEARTBEAT_INTERVAL, MAX_MULTIPATH_PATHS, PATH_MAX_IDLE_TI /// performance at lower bandwidths and latencies. The default configuration is tuned for a 100Mbps /// link with a 100ms round trip time. /// +/// Use the [`QuicTransportConfigBuilder`] to customize these tunable fields. +/// /// In iroh, the config has some specific default values that make iroh's holepunching work /// well with QUIC multipath. Adjusting those settings may cause suboptimal usage. /// @@ -108,29 +114,31 @@ use crate::magicsock::{HEARTBEAT_INTERVAL, MAX_MULTIPATH_PATHS, PATH_MAX_IDLE_TI /// - [`QuicTransportConfigBuilder::default_path_max_idle_timeout`] /// - [`QuicTransportConfigBuilder::max_concurrent_multipath_paths`] /// - [`QuicTransportConfigBuilder::set_max_remote_nat_traversal_addresses`] +/// +/// # Examples +/// ``` +/// use std::time::Duration; +/// +/// use iroh::endpoint::QuicTransportConfig; +/// +/// let _cfg = QuicTransportConfig::builder() +/// .max_idle_timeout(Duration::from_secs(35)) +/// .build(); +/// ``` #[derive(Debug, Clone)] -pub struct QuicTransportConfigBuilder(quinn::TransportConfig); +pub struct QuicTransportConfig(Arc); -impl Default for QuicTransportConfigBuilder { - fn default() -> Self { - let mut cfg = quinn::TransportConfig::default(); - // Override some transport config settings. - cfg.keep_alive_interval(Some(HEARTBEAT_INTERVAL)); - cfg.default_path_keep_alive_interval(Some(HEARTBEAT_INTERVAL)); - cfg.default_path_max_idle_timeout(Some(PATH_MAX_IDLE_TIMEOUT)); - cfg.max_concurrent_multipath_paths(MAX_MULTIPATH_PATHS + 1); - cfg.set_max_remote_nat_traversal_addresses(MAX_MULTIPATH_PATHS as u8); - Self(cfg) +impl QuicTransportConfig { + /// Returns a default [`QuicTransportConfigBuilder`] that allows customizing + /// a [`QuicTransportConfig`]. + pub fn builder() -> QuicTransportConfigBuilder { + QuicTransportConfigBuilder::new() } } -/// todo: docs -#[derive(Debug, Clone)] -pub struct QuicTransportConfig(Arc); - impl Default for QuicTransportConfig { fn default() -> Self { - QuicTransportConfigBuilder::default().build() + QuicTransportConfigBuilder::new().build() } } @@ -141,7 +149,19 @@ impl QuicTransportConfig { } impl QuicTransportConfigBuilder { - /// Todo: docs + /// Create a default [`QuicTransportConfigBuilder`]. + fn new() -> Self { + let mut cfg = quinn::TransportConfig::default(); + // Override some transport config settings. + cfg.keep_alive_interval(Some(HEARTBEAT_INTERVAL)); + cfg.default_path_keep_alive_interval(Some(HEARTBEAT_INTERVAL)); + cfg.default_path_max_idle_timeout(Some(PATH_MAX_IDLE_TIMEOUT)); + cfg.max_concurrent_multipath_paths(MAX_MULTIPATH_PATHS + 1); + cfg.set_max_remote_nat_traversal_addresses(MAX_MULTIPATH_PATHS as u8); + Self(cfg) + } + + /// Build a [`QuicTransportConfig`] from the builder. pub fn build(self) -> QuicTransportConfig { QuicTransportConfig(Arc::new(self.0)) } @@ -152,13 +172,13 @@ impl QuicTransportConfigBuilder { /// /// Worst-case memory use is directly proportional to `max_concurrent_bidi_streams * /// stream_receive_window`, with an upper bound proportional to `receive_window`. - pub fn max_concurrent_bidi_streams(&mut self, value: VarInt) -> &mut Self { + pub fn max_concurrent_bidi_streams(mut self, value: VarInt) -> Self { self.0.max_concurrent_bidi_streams(value); self } /// Variant of `max_concurrent_bidi_streams` affecting unidirectional streams. - pub fn max_concurrent_uni_streams(&mut self, value: VarInt) -> &mut Self { + pub fn max_concurrent_uni_streams(mut self, value: VarInt) -> Self { self.0.max_concurrent_uni_streams(value); self } @@ -185,7 +205,7 @@ impl QuicTransportConfigBuilder { /// # Ok(()) /// # } /// ``` - pub fn max_idle_timeout(&mut self, value: Option) -> &mut Self { + pub fn max_idle_timeout(mut self, value: Option) -> Self { self.0.max_idle_timeout(value); self } @@ -198,7 +218,7 @@ impl QuicTransportConfigBuilder { /// stream doesn't monopolize receive buffers, which may otherwise occur if the application /// chooses not to read from a large stream for a time while still requiring data on other /// streams. - pub fn stream_receive_window(&mut self, value: VarInt) -> &mut Self { + pub fn stream_receive_window(mut self, value: VarInt) -> Self { self.0.stream_receive_window(value); self } @@ -209,7 +229,7 @@ impl QuicTransportConfigBuilder { /// This should be set to at least the expected connection latency multiplied by the maximum /// desired throughput. Larger values can be useful to allow maximum throughput within a /// stream while another is blocked. - pub fn receive_window(&mut self, value: VarInt) -> &mut Self { + pub fn receive_window(mut self, value: VarInt) -> Self { self.0.receive_window(value); self } @@ -220,7 +240,7 @@ impl QuicTransportConfigBuilder { /// flow control credit. Endpoints that wish to handle large numbers of connections robustly /// should take care to set this low enough to guarantee memory exhaustion does not occur if /// every connection uses the entire window. - pub fn send_window(&mut self, value: u64) -> &mut Self { + pub fn send_window(mut self, value: u64) -> Self { self.0.send_window(value); self } @@ -235,27 +255,27 @@ impl QuicTransportConfigBuilder { /// /// Disabling fairness can reduce fragmentation and protocol overhead for workloads that use /// many small streams. - pub fn send_fairness(&mut self, value: bool) -> &mut Self { + pub fn send_fairness(mut self, value: bool) -> Self { self.0.send_fairness(value); self } /// Maximum reordering in packet number space before FACK style loss detection considers a /// packet lost. Should not be less than 3, per RFC5681. - pub fn packet_threshold(&mut self, value: u32) -> &mut Self { + pub fn packet_threshold(mut self, value: u32) -> Self { self.0.packet_threshold(value); self } /// Maximum reordering in time space before time based loss detection considers a packet lost, /// as a factor of RTT. - pub fn time_threshold(&mut self, value: f32) -> &mut Self { + pub fn time_threshold(mut self, value: f32) -> Self { self.0.time_threshold(value); self } /// The RTT used before an RTT sample is taken. - pub fn initial_rtt(&mut self, value: Duration) -> &mut Self { + pub fn initial_rtt(mut self, value: Duration) -> Self { self.0.initial_rtt(value); self } @@ -268,7 +288,7 @@ impl QuicTransportConfigBuilder { /// exceeding the network path's IP MTU. If the provided value is higher than what the network /// path actually supports, packet loss will eventually trigger black hole detection and bring /// it down to [`QuicTransportConfigBuilder::min_mtu`]. - pub fn initial_mtu(&mut self, value: u16) -> &mut Self { + pub fn initial_mtu(mut self, value: u16) -> Self { self.0.initial_mtu(value); self } @@ -286,7 +306,7 @@ impl QuicTransportConfigBuilder { /// [`QuicTransportConfigBuilder::initial_mtu`] together with /// [`QuicTransportConfigBuilder::mtu_discovery_config`] to set a maximum UDP payload size that robustly /// adapts to the network. - pub fn min_mtu(&mut self, value: u16) -> &mut Self { + pub fn min_mtu(mut self, value: u16) -> Self { self.0.min_mtu(value); self } @@ -294,7 +314,7 @@ impl QuicTransportConfigBuilder { /// Specifies the MTU discovery config (see [`MtuDiscoveryConfig`] for details). /// /// Enabled by default. - pub fn mtu_discovery_config(&mut self, value: Option) -> &mut Self { + pub fn mtu_discovery_config(mut self, value: Option) -> Self { self.0.mtu_discovery_config(value); self } @@ -308,7 +328,7 @@ impl QuicTransportConfigBuilder { /// well as the total size of stream write bursts can be inferred by observers under certain /// conditions. This analysis requires either an uncongested connection or application datagrams /// too large to be coalesced. - pub fn pad_to_mtu(&mut self, value: bool) -> &mut Self { + pub fn pad_to_mtu(mut self, value: bool) -> Self { self.0.pad_to_mtu(value); self } @@ -321,13 +341,13 @@ impl QuicTransportConfigBuilder { /// Defaults to `None`, which disables controlling the peer's acknowledgement frequency. Even /// if set to `None`, the local side still supports the acknowledgement frequency QUIC /// extension and may use it in other ways. - pub fn ack_frequency_config(&mut self, value: Option) -> &mut Self { + pub fn ack_frequency_config(mut self, value: Option) -> Self { self.0.ack_frequency_config(value); self } /// Number of consecutive PTOs after which network is considered to be experiencing persistent congestion. - pub fn persistent_congestion_threshold(&mut self, value: u32) -> &mut Self { + pub fn persistent_congestion_threshold(mut self, value: u32) -> Self { self.0.persistent_congestion_threshold(value); self } @@ -339,13 +359,13 @@ impl QuicTransportConfigBuilder { /// `None` to disable, which is the default. Only one side of any given connection needs keep-alive /// enabled for the connection to be preserved. Must be set lower than the idle_timeout of both /// peers to be effective. - pub fn keep_alive_interval(&mut self, value: Duration) -> &mut Self { + pub fn keep_alive_interval(mut self, value: Duration) -> Self { self.0.keep_alive_interval(Some(value)); self } /// Maximum quantity of out-of-order crypto layer data to buffer. - pub fn crypto_buffer_size(&mut self, value: usize) -> &mut Self { + pub fn crypto_buffer_size(mut self, value: usize) -> Self { self.0.crypto_buffer_size(value); self } @@ -354,7 +374,7 @@ impl QuicTransportConfigBuilder { /// /// This allows passive observers to easily judge the round trip time of a connection, which can /// be useful for network administration but sacrifices a small amount of privacy. - pub fn allow_spin(&mut self, value: bool) -> &mut Self { + pub fn allow_spin(mut self, value: bool) -> Self { self.0.allow_spin(value); self } @@ -365,7 +385,7 @@ impl QuicTransportConfigBuilder { /// The peer is forbidden to send single datagrams larger than this size. If the aggregate size /// of all datagrams that have been received from the peer but not consumed by the application /// exceeds this value, old datagrams are dropped until it is no longer exceeded. - pub fn datagram_receive_buffer_size(&mut self, value: Option) -> &mut Self { + pub fn datagram_receive_buffer_size(mut self, value: Option) -> Self { self.0.datagram_receive_buffer_size(value); self } @@ -376,7 +396,7 @@ impl QuicTransportConfigBuilder { /// than the link, or even the underlying hardware, can transmit them. This limits the amount of /// memory that may be consumed in that case. When the send buffer is full and a new datagram is /// sent, older datagrams are dropped until sufficient space is available. - pub fn datagram_send_buffer_size(&mut self, value: usize) -> &mut Self { + pub fn datagram_send_buffer_size(mut self, value: usize) -> Self { self.0.datagram_send_buffer_size(value); self } @@ -393,9 +413,9 @@ impl QuicTransportConfigBuilder { /// config.congestion_controller_factory(Arc::new(congestion::NewRenoConfig::default())); /// ``` pub fn congestion_controller_factory( - &mut self, + mut self, factory: Arc, - ) -> &mut Self { + ) -> Self { self.0.congestion_controller_factory(factory); self } @@ -410,7 +430,7 @@ impl QuicTransportConfigBuilder { /// by all network interface drivers or packet inspection tools. `quinn-udp` will attempt to /// disable GSO automatically when unavailable, but this can lead to spurious packet loss at /// startup, temporarily degrading performance. - pub fn enable_segmentation_offload(&mut self, enabled: bool) -> &mut Self { + pub fn enable_segmentation_offload(mut self, enabled: bool) -> Self { self.0.enable_segmentation_offload(enabled); self } @@ -419,7 +439,7 @@ impl QuicTransportConfigBuilder { /// /// This will aid peers in inferring their reachable address, which in most NATd networks /// will not be easily available to them. - pub fn send_observed_address_reports(&mut self, enabled: bool) -> &mut Self { + pub fn send_observed_address_reports(mut self, enabled: bool) -> Self { self.0.send_observed_address_reports(enabled); self } @@ -430,7 +450,7 @@ impl QuicTransportConfigBuilder { /// address reports will do so if this transport parameter is set. In general, observed address /// reports cannot be trusted. This, however, can aid the current endpoint in inferring its /// reachable address, which in most NATd networks will not be easily available. - pub fn receive_observed_address_reports(&mut self, enabled: bool) -> &mut Self { + pub fn receive_observed_address_reports(mut self, enabled: bool) -> Self { self.0.receive_observed_address_reports(enabled); self } @@ -445,7 +465,7 @@ impl QuicTransportConfigBuilder { /// enable multipath as well. /// /// Note: this method will ignore values less than the recommended 13 and will log a warning. - pub fn max_concurrent_multipath_paths(&mut self, max_concurrent: u32) -> &mut Self { + pub fn max_concurrent_multipath_paths(mut self, max_concurrent: u32) -> Self { if max_concurrent < MAX_MULTIPATH_PATHS + 1 { warn!( "QuicTransportConfig::max_concurrent_multipath_paths must be at minimum {}, ignoring user supplied value", @@ -464,7 +484,7 @@ impl QuicTransportConfigBuilder { /// abandoned the entire connection will be closed. /// /// Note: this method will ignore values higher than the recommended 6500 ms and will log a warning. - pub fn default_path_max_idle_timeout(&mut self, timeout: Duration) -> &mut Self { + pub fn default_path_max_idle_timeout(mut self, timeout: Duration) -> Self { if timeout > PATH_MAX_IDLE_TIMEOUT { warn!( "QuicTransportConfig::default_path_max_idle must be at most {:?}, ignoring user supplied value", @@ -484,7 +504,7 @@ impl QuicTransportConfigBuilder { /// control over which path is used for this. /// /// Note: this method will ignore values higher than the recommended 5 seconds and will log a warning. - pub fn default_path_keep_alive_interval(&mut self, interval: Duration) -> &mut Self { + pub fn default_path_keep_alive_interval(mut self, interval: Duration) -> Self { if interval > HEARTBEAT_INTERVAL { warn!( "QuicTransportConfig::default_path_keep_alive must be at most {:?}, ignoring user supplied value", @@ -508,7 +528,7 @@ impl QuicTransportConfigBuilder { /// 12 will be used. /// /// Note: this method will ignore values less than the recommended 12 and will log a warning. - pub fn set_max_remote_nat_traversal_addresses(&mut self, max_addresses: u8) -> &mut Self { + pub fn set_max_remote_nat_traversal_addresses(mut self, max_addresses: u8) -> Self { if max_addresses < MAX_MULTIPATH_PATHS as u8 { warn!( "QuicTransportConfig::max_remote_nat_traversal_addresses must be at least {}, ignoring user supplied value", @@ -525,7 +545,7 @@ impl QuicTransportConfigBuilder { /// This assigns a [`QlogFactory`] that produces qlog capture configurations for /// individual connections. #[cfg(feature = "qlog")] - pub fn qlog_factory(&mut self, factory: Arc) -> &mut Self { + pub fn qlog_factory(mut self, factory: Arc) -> Self { self.0.qlog_factory(factory); self } @@ -540,7 +560,7 @@ impl QuicTransportConfigBuilder { /// /// The files will be prefixed with `prefix`. #[cfg(feature = "qlog")] - pub fn qlog_from_env(&mut self, prefix: &str) -> &mut Self { + pub fn qlog_from_env(mut self, prefix: &str) -> Self { self.0.qlog_from_env(prefix); self } @@ -550,16 +570,25 @@ impl QuicTransportConfigBuilder { /// This uses [`QlogFileFactory`] to create a factory to write qlog traces into /// the specified directory. The files will be prefixed with `prefix`. #[cfg(feature = "qlog")] - pub fn qlog_from_path(&mut self, path: impl AsRef, prefix: &str) -> &mut Self { + pub fn qlog_from_path(mut self, path: impl AsRef, prefix: &str) -> Self { self.0.qlog_from_path(path, prefix); self } } -/// Parameters governing incoming connections. +/// A builder for a [`ServerConfig`]. +#[derive(Debug, Clone)] +pub struct ServerConfigBuilder { + inner: quinn::ServerConfig, + transport: QuicTransportConfig, +} + +/// Parameters governing incoming connections /// /// Default values should be suitable for most internet applications. /// +/// Use a [`ServerConfigBuilder`] to adjust the default values. +/// /// To create a [`ServerConfig`] compatible with your [`Endpoint`] identity, use the [`Endpoint::create_server_config_builder`] method. /// /// [`Endpoint`]: crate::Endpoint @@ -567,13 +596,6 @@ impl QuicTransportConfigBuilder { // Note: used in `iroh::endpoint::connection::Incoming::accept_with` // This is new-typed since `quinn::ServerConfig` takes a `TransportConfig`, which we new-type as a `QuicTransportConfig` #[derive(Debug, Clone)] -pub struct ServerConfigBuilder { - inner: quinn::ServerConfig, - transport: QuicTransportConfig, -} - -/// todo: docs -#[derive(Debug, Clone)] pub struct ServerConfig(Arc); impl ServerConfig { @@ -598,7 +620,7 @@ impl ServerConfig { } impl ServerConfigBuilder { - /// todo: docs + /// Build a [`ServerConfig`] from a [`ServerConfigBuilder`]. pub fn build(self) -> ServerConfig { ServerConfig(Arc::new(self.inner)) } @@ -607,39 +629,21 @@ impl ServerConfigBuilder { Self { inner, transport } } - /// Transport configuration used for incoming connections. - pub fn transport_config(&self) -> QuicTransportConfig { - self.transport.clone() - } - - /// TLS configuration used for incoming connections. - pub fn crypto(&self) -> Arc { - self.inner.crypto.clone() - } - - /// Configuration for sending and handling validation tokens. - pub fn validation_token(&self) -> ValidationTokenConfig { - self.inner.validation_token.clone() - } - /// Sets a custom [`QuicTransportConfig`]. - pub fn set_transport_config(&mut self, transport: QuicTransportConfig) -> &mut Self { + pub fn set_transport_config(mut self, transport: QuicTransportConfig) -> Self { self.inner.transport_config(transport.to_inner_arc()); self.transport = transport; self } /// Sets a custom [`ValidationTokenConfig`]. - pub fn set_validation_token_config( - &mut self, - validation_token: ValidationTokenConfig, - ) -> &mut Self { + pub fn set_validation_token_config(mut self, validation_token: ValidationTokenConfig) -> Self { self.inner.validation_token_config(validation_token); self } /// Private key used to authenticate data included in handshake tokens - pub fn set_token_key(&mut self, value: Arc) -> &mut Self { + pub fn set_token_key(mut self, value: Arc) -> Self { self.inner.token_key(value); self } @@ -647,7 +651,7 @@ impl ServerConfigBuilder { /// Duration after a retry token was issued for which it's considered valid /// /// Defaults to 15 seconds. - pub fn set_retry_token_lifetime(&mut self, value: Duration) -> &mut Self { + pub fn set_retry_token_lifetime(mut self, value: Duration) -> Self { self.inner.retry_token_lifetime(value); self } @@ -665,7 +669,7 @@ impl ServerConfigBuilder { /// exhaustion in most contexts. /// /// [`Incoming`]: crate::endpoint::Incoming - pub fn set_max_incoming(&mut self, max_incoming: usize) -> &mut Self { + pub fn set_max_incoming(mut self, max_incoming: usize) -> Self { self.inner.max_incoming(max_incoming); self } @@ -683,7 +687,7 @@ impl ServerConfigBuilder { /// [`Incoming`]. /// /// [`Incoming`]: crate::endpoint::Incoming - pub fn set_incoming_buffer_size(&mut self, incoming_buffer_size: u64) -> &mut Self { + pub fn set_incoming_buffer_size(mut self, incoming_buffer_size: u64) -> Self { self.inner.incoming_buffer_size(incoming_buffer_size); self } @@ -701,7 +705,7 @@ impl ServerConfigBuilder { /// exhaustion in most contexts. /// /// [`Incoming`]: crate::endpoint::Incoming - pub fn set_incoming_buffer_size_total(&mut self, incoming_buffer_size_total: u64) -> &mut Self { + pub fn set_incoming_buffer_size_total(mut self, incoming_buffer_size_total: u64) -> Self { self.inner .incoming_buffer_size_total(incoming_buffer_size_total); self @@ -714,7 +718,7 @@ impl ServerConfigBuilder { /// Defaults to [`quinn::StdSystemTime`], which simply calls [`SystemTime::now()`](std::time::SystemTime::now). /// /// [`SystemTime`]: std::time::SystemTime - pub fn set_time_source(&mut self, time_source: Arc) -> &mut Self { + pub fn set_time_source(mut self, time_source: Arc) -> Self { self.inner.time_source(time_source); self } diff --git a/iroh/src/test_utils/qlog.rs b/iroh/src/test_utils/qlog.rs index a477cbea323..321b0be333d 100644 --- a/iroh/src/test_utils/qlog.rs +++ b/iroh/src/test_utils/qlog.rs @@ -8,9 +8,9 @@ use n0_error::Result; #[cfg(feature = "qlog")] use n0_future::time::Instant; -use crate::endpoint::QuicTransportConfig; #[cfg(feature = "qlog")] -use crate::endpoint::{QlogFileFactory, QuicTransportConfigBuilder}; +use crate::endpoint::QlogFileFactory; +use crate::endpoint::QuicTransportConfig; /// Builder to create one or more related qlog configs. /// @@ -72,14 +72,14 @@ impl QlogFileGroup { } #[cfg(feature = "qlog")] { - let mut builder = QuicTransportConfigBuilder::default(); + let mut builder = QuicTransportConfig::builder(); if std::env::var("IROH_TEST_QLOG").is_ok() { let prefix = format!("{}.{}", self.title, name.to_string()); let factory = QlogFileFactory::new(self.directory.clone()) .with_prefix(prefix) .with_start_instant(self.start.into()); - builder.qlog_factory(Arc::new(factory)); + builder = builder.qlog_factory(Arc::new(factory)); } Ok(builder.build()) } From bd98c5e4a270786608be68526f8d560382b953a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cramfox=E2=80=9D?= <“kasey@n0.computer”> Date: Mon, 15 Dec 2025 15:24:06 -0500 Subject: [PATCH 7/9] re-export `UnorderedRecvStream` --- Cargo.lock | 28 ++++++++++++++-------------- iroh/src/endpoint.rs | 2 +- iroh/src/endpoint/quic.rs | 1 + 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 509e2cd7788..f79b1cde266 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -580,9 +580,9 @@ dependencies = [ [[package]] name = "console" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b430743a6eb14e9764d4260d4c0d8123087d504eeb9c48f2b2a5e810dd369df4" +checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4" dependencies = [ "encode_unicode", "libc", @@ -1241,9 +1241,9 @@ dependencies = [ [[package]] name = "fs-err" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62d91fd049c123429b018c47887d3f75a265540dd3c30ba9cb7bae9197edb03a" +checksum = "824f08d01d0f496b3eca4f001a13cf17690a6ee930043d20817f547455fd98f8" dependencies = [ "autocfg", "tokio", @@ -1591,9 +1591,9 @@ dependencies = [ [[package]] name = "governor" -version = "0.10.2" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e23d5986fd4364c2fb7498523540618b4b8d92eec6c36a02e565f66748e2f79" +checksum = "9efcab3c1958580ff1f25a2a41be1668f7603d849bb63af523b208a3cc1223b8" dependencies = [ "cfg-if", "dashmap", @@ -2541,9 +2541,9 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" dependencies = [ "bitflags", "libc", @@ -3537,9 +3537,9 @@ dependencies = [ [[package]] name = "rcgen" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fae430c6b28f1ad601274e78b7dffa0546de0b73b4cd32f46723c0c2a16f7a5" +checksum = "3ec0a99f2de91c3cddc84b37e7db80e4d96b743e05607f647eb236fc0455907f" dependencies = [ "pem", "ring", @@ -3626,9 +3626,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.25" +version = "0.12.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6eff9328d40131d43bd911d42d79eb6a47312002a4daefc9e37f17e74a7701a" +checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" dependencies = [ "base64", "bytes", @@ -4216,9 +4216,9 @@ dependencies = [ [[package]] name = "sorted-index-buffer" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d96a9d278ef78c991bf2faabd578b41199037846ff96fbb093f84bb50adaa00" +checksum = "ea06cc588e43c632923a55450401b8f25e628131571d4e1baea1bdfdb2b5ed06" [[package]] name = "spez" diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index a71af1b5f0a..07bdb13303d 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -67,7 +67,7 @@ pub use self::{ ReadExactError, ReadToEndError, RecvStream, ResetError, RttEstimator, SendDatagram, SendDatagramError, SendStream, ServerConfig, ServerConfigBuilder, Session, Side, StoppedError, StreamId, TimeSource, TokenLog, TokenReuseError, TransportError, - TransportErrorCode, TransportParameters, UdpStats, UnsupportedVersion, + TransportErrorCode, TransportParameters, UdpStats, UnorderedRecvStream, UnsupportedVersion, ValidationTokenConfig, VarInt, VarIntBoundsExceeded, WriteError, Written, }, }; diff --git a/iroh/src/endpoint/quic.rs b/iroh/src/endpoint/quic.rs index 198d1ee0e82..978e86a7edb 100644 --- a/iroh/src/endpoint/quic.rs +++ b/iroh/src/endpoint/quic.rs @@ -37,6 +37,7 @@ pub use quinn::{ Side, // iroh::endpoint::Connection, quinn::StreamId, StoppedError, // quinn::SendStream StreamId, // quinn::RecvStream + UnorderedRecvStream, // quinn::RecvStream VarInt, // various VarIntBoundsExceeded, // quinn::VarInt, quinn::IdleTimeout WriteError, // quinn::SendStream From ecb6f71b62b51632fc6e762b9a5c9fba15536d3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cramfox=E2=80=9D?= <“kasey@n0.computer”> Date: Mon, 15 Dec 2025 15:50:30 -0500 Subject: [PATCH 8/9] remove the accessor methods in `ServerConfig` --- iroh/src/endpoint.rs | 20 ++++++++++---------- iroh/src/endpoint/quic.rs | 20 +------------------- 2 files changed, 11 insertions(+), 29 deletions(-) diff --git a/iroh/src/endpoint.rs b/iroh/src/endpoint.rs index 07bdb13303d..afcd7f97ee1 100644 --- a/iroh/src/endpoint.rs +++ b/iroh/src/endpoint.rs @@ -59,16 +59,16 @@ pub use self::{ }, quic::{ AcceptBi, AcceptUni, AckFrequencyConfig, AeadKey, ApplicationClose, Chunk, ClosedStream, - Codec, ConnectionClose, ConnectionError, ConnectionId, ConnectionStats, Controller, - ControllerFactory, ControllerMetrics, CryptoError, CryptoServerConfig, Dir, - ExportKeyingMaterialError, FrameStats, FrameType, HandshakeTokenKey, HeaderKey, - IdleTimeout, Keys, MtuDiscoveryConfig, OpenBi, OpenUni, PacketKey, PathId, PathStats, - QuicConnectError, QuicTransportConfig, QuicTransportConfigBuilder, ReadDatagram, ReadError, - ReadExactError, ReadToEndError, RecvStream, ResetError, RttEstimator, SendDatagram, - SendDatagramError, SendStream, ServerConfig, ServerConfigBuilder, Session, Side, - StoppedError, StreamId, TimeSource, TokenLog, TokenReuseError, TransportError, - TransportErrorCode, TransportParameters, UdpStats, UnorderedRecvStream, UnsupportedVersion, - ValidationTokenConfig, VarInt, VarIntBoundsExceeded, WriteError, Written, + Codec, ConnectionClose, ConnectionError, ConnectionStats, Controller, ControllerFactory, + ControllerMetrics, CryptoError, Dir, ExportKeyingMaterialError, FrameStats, FrameType, + HandshakeTokenKey, HeaderKey, IdleTimeout, Keys, MtuDiscoveryConfig, OpenBi, OpenUni, + PacketKey, PathId, PathStats, QuicConnectError, QuicTransportConfig, + QuicTransportConfigBuilder, ReadDatagram, ReadError, ReadExactError, ReadToEndError, + RecvStream, ResetError, RttEstimator, SendDatagram, SendDatagramError, SendStream, + ServerConfig, ServerConfigBuilder, Side, StoppedError, StreamId, TimeSource, TokenLog, + TokenReuseError, TransportError, TransportErrorCode, TransportParameters, UdpStats, + UnorderedRecvStream, UnsupportedVersion, ValidationTokenConfig, VarInt, + VarIntBoundsExceeded, WriteError, Written, }, }; pub use crate::magicsock::transports::TransportConfig; diff --git a/iroh/src/endpoint/quic.rs b/iroh/src/endpoint/quic.rs index 978e86a7edb..9843c359851 100644 --- a/iroh/src/endpoint/quic.rs +++ b/iroh/src/endpoint/quic.rs @@ -52,7 +52,6 @@ pub use quinn_proto::{ Chunk, // quinn::RecvStream ConnectError as QuicConnectError, // iroh::endpoint::ConnectWithOptsError ConnectionClose, // quinn::ConnectionError - ConnectionId, // quinn_proto::crypto::ServerConfig FrameStats, // quinn::ConnectionStats FrameType, // quinn_proto::TransportError PathId, // quinn_proto::crypto::PacketKey @@ -71,15 +70,13 @@ pub use quinn_proto::{ ControllerMetrics, // quinn_proto::congestion::Controller }, crypto::{ - AeadKey, // quinn::HandshakeTokenKey + AeadKey, // quinn::HandshakeTokenKey CryptoError, // quinn_proto::crypto::CryptoError, quinn_proto::crypto::PacketKey ExportKeyingMaterialError, // iroh::endpoint::Connection HandshakeTokenKey, // iroh::endpoint::quic::ServerConfig HeaderKey, // quinn_proto::crypto::Keys Keys, // quinn_proto::crypto::Session PacketKey, // quinn_proto::crypto::Keys - ServerConfig as CryptoServerConfig, // iroh::endpoint::quic::ServerConfig - Session, // quinn_proto::crypto::ServerConfig UnsupportedVersion, // quinn_proto::ConnectError }, transport_parameters::TransportParameters, // quinn_proto::crypot::ServerConfig @@ -603,21 +600,6 @@ impl ServerConfig { pub(crate) fn to_inner_arc(&self) -> Arc { self.0.clone() } - - /// Transport configuration used for incoming connections. - pub fn transport_config(&self) -> QuicTransportConfig { - QuicTransportConfig(self.0.transport.clone()) - } - - /// TLS configuration used for incoming connections. - pub fn crypto(&self) -> Arc { - self.0.crypto.clone() - } - - /// Configuration for sending and handling validation tokens. - pub fn validation_token(&self) -> ValidationTokenConfig { - self.0.validation_token.clone() - } } impl ServerConfigBuilder { From 69e22d9f660dfde763a6d18648d4184d84d4eda1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cramfox=E2=80=9D?= <“kasey@n0.computer”> Date: Mon, 15 Dec 2025 16:05:21 -0500 Subject: [PATCH 9/9] fix: doc tests --- iroh/src/endpoint/quic.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/iroh/src/endpoint/quic.rs b/iroh/src/endpoint/quic.rs index 9843c359851..12b0fcaa588 100644 --- a/iroh/src/endpoint/quic.rs +++ b/iroh/src/endpoint/quic.rs @@ -120,7 +120,7 @@ pub struct QuicTransportConfigBuilder(quinn::TransportConfig); /// use iroh::endpoint::QuicTransportConfig; /// /// let _cfg = QuicTransportConfig::builder() -/// .max_idle_timeout(Duration::from_secs(35)) +/// .send_observed_address_reports(true) /// .build(); /// ``` #[derive(Debug, Clone)] @@ -193,13 +193,15 @@ impl QuicTransportConfigBuilder { /// # use std::{convert::TryInto, time::Duration}; /// # use iroh::endpoint::{QuicTransportConfig, VarInt, VarIntBoundsExceeded}; /// # fn main() -> Result<(), VarIntBoundsExceeded> { - /// let mut config = QuicTransportConfig::default(); - /// - /// // Set the idle timeout as `VarInt`-encoded milliseconds - /// config.max_idle_timeout(Some(VarInt::from_u32(10_000).into())); + /// let mut builder = QuicTransportConfig::builder() + /// // Set the idle timeout as `VarInt`-encoded milliseconds + /// .max_idle_timeout(Some(VarInt::from_u32(10_000).into())); /// /// // Set the idle timeout as a `Duration` - /// config.max_idle_timeout(Some(Duration::from_secs(10).try_into()?)); + /// builder = builder.max_idle_timeout(Some(Duration::from_secs(10).try_into()?)); + /// + /// let _cfg = builder.build(); + /// /// # Ok(()) /// # } /// ``` @@ -407,8 +409,9 @@ impl QuicTransportConfigBuilder { /// # Example /// ``` /// # use iroh::endpoint::QuicTransportConfig; use quinn_proto::congestion; use std::sync::Arc; - /// let mut config = QuicTransportConfig::default(); - /// config.congestion_controller_factory(Arc::new(congestion::NewRenoConfig::default())); + /// let config = QuicTransportConfig::builder() + /// .congestion_controller_factory(Arc::new(congestion::NewRenoConfig::default())) + /// .build(); /// ``` pub fn congestion_controller_factory( mut self,