diff --git a/msg-socket/src/req/socket.rs b/msg-socket/src/req/socket.rs index 308983d..e03e9f2 100644 --- a/msg-socket/src/req/socket.rs +++ b/msg-socket/src/req/socket.rs @@ -64,7 +64,7 @@ where response_rx.await.map_err(|_| ReqError::SocketClosed)? } - /// Connects to the target with the default options. WARN: this will block until the connection can be established. + /// Connects to the target with the default options. WARN: this will wait until the connection can be established. pub async fn connect(&mut self, endpoint: SocketAddr) -> Result<(), ReqError> { // Initialize communication channels let (to_driver, from_socket) = mpsc::channel(DEFAULT_BUFFER_SIZE); diff --git a/msg-transport/src/quic/config.rs b/msg-transport/src/quic/config.rs index 1597eb7..2f80771 100644 --- a/msg-transport/src/quic/config.rs +++ b/msg-transport/src/quic/config.rs @@ -1,4 +1,4 @@ -use quinn::IdleTimeout; +use quinn::{congestion::ControllerFactory, IdleTimeout}; use std::{sync::Arc, time::Duration}; use super::tls::{self_signed_certificate, unsafe_client_config}; @@ -11,6 +11,114 @@ pub struct Config { pub server_config: quinn::ServerConfig, } +#[derive(Debug)] +pub struct ConfigBuilder { + cc: C, + initial_mtu: u16, + max_stream_bandwidth: u32, + expected_rtt: u32, + max_idle_timeout: Duration, + keep_alive_interval: Duration, +} + +impl ConfigBuilder +where + C: ControllerFactory + Default + Send + Sync + 'static, +{ + /// Creates a new [`ConfigBuilder`] with sensible defaults. + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + Self { + cc: C::default(), + initial_mtu: 1460, + max_stream_bandwidth: 50 * MiB, + expected_rtt: 100, + max_idle_timeout: Duration::from_secs(60 * 5), + keep_alive_interval: Duration::from_secs(15), + } + } + + /// Sets the initial MTU. + pub fn initial_mtu(mut self, mtu: u16) -> Self { + self.initial_mtu = mtu; + self + } + + /// Sets the maximum stream bandwidth in bytes per second. + pub fn max_stream_bandwidth(mut self, bandwidth: u32) -> Self { + self.max_stream_bandwidth = bandwidth; + self + } + + /// Sets the expected round-trip time in milliseconds. + pub fn expected_rtt(mut self, rtt: u32) -> Self { + self.expected_rtt = rtt; + self + } + + /// Sets the maximum idle timeout. + pub fn max_idle_timeout(mut self, timeout: Duration) -> Self { + self.max_idle_timeout = timeout; + self + } + + /// Sets the keep-alive interval. + pub fn keep_alive_interval(mut self, interval: Duration) -> Self { + self.keep_alive_interval = interval; + self + } + + /// Sets the congestion controller. + pub fn congestion_controller(mut self, cc: C) -> Self { + self.cc = cc; + self + } + + /// Builds the QUIC [`Config`]. + pub fn build(self) -> Config { + let mut transport = quinn::TransportConfig::default(); + + // Stream receive window + let stream_rwnd = self.max_stream_bandwidth / 1000 * self.expected_rtt; + + transport + .keep_alive_interval(Some(self.keep_alive_interval)) + .max_idle_timeout(Some( + IdleTimeout::try_from(self.max_idle_timeout).expect("Valid idle timeout"), + )) + // Disable datagram support + .datagram_receive_buffer_size(None) + .datagram_send_buffer_size(0) + .max_concurrent_uni_streams(0u32.into()) + .initial_mtu(self.initial_mtu) + .min_mtu(self.initial_mtu) + .allow_spin(false) + .stream_receive_window((8 * stream_rwnd).into()) + .congestion_controller_factory(self.cc) + .initial_rtt(Duration::from_millis(self.expected_rtt.into())) + .send_window((8 * stream_rwnd).into()); + + let transport = Arc::new(transport); + let (cert, key) = self_signed_certificate(); + + let mut server_config = + quinn::ServerConfig::with_single_cert(cert, key).expect("Valid rustls config"); + + server_config.use_retry(true); + server_config.transport_config(Arc::clone(&transport)); + + let mut client_config = quinn::ClientConfig::new(Arc::new(unsafe_client_config())); + + client_config.transport_config(transport); + + Config { + endpoint_config: quinn::EndpointConfig::default(), + client_config, + server_config, + } + } +} + impl Default for Config { fn default() -> Self { // The expected RTT in ms. This has a big impact on initial performance. @@ -23,7 +131,7 @@ impl Default for Config { // Default initial window is 12000 bytes. This is limited to not overwhelm slow links. let mut cc = quinn::congestion::CubicConfig::default(); - // 4 MiB initial window + // 1 MiB initial window // Note that this is a very high initial window outside of private networks. // TODO: document this and make it configurable cc.initial_window(MiB as u64); diff --git a/msg-transport/src/quic/mod.rs b/msg-transport/src/quic/mod.rs index 0702026..6b5185a 100644 --- a/msg-transport/src/quic/mod.rs +++ b/msg-transport/src/quic/mod.rs @@ -19,7 +19,8 @@ mod config; mod stream; mod tls; -use config::Config; +pub use config::{Config, ConfigBuilder}; +pub use quinn::congestion; use stream::QuicStream; /// A QUIC error.