From 8bd903dcf6b3def1ab097b060f5e8688c87f568e Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 10 Feb 2023 00:27:14 +0000 Subject: [PATCH 01/60] Init (it compiles) --- Cargo.lock | 48 +++++ transports/quic/Cargo.toml | 2 + transports/quic/src/connection.rs | 68 +++++- transports/quic/src/connection/connecting.rs | 89 ++++---- transports/quic/src/connection/substream.rs | 59 ++++- transports/quic/src/endpoint.rs | 10 +- transports/quic/src/transport.rs | 215 +++++++++++-------- 7 files changed, 331 insertions(+), 160 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 014cc268b0b..8db906e6e56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2613,6 +2613,7 @@ dependencies = [ "log", "parking_lot 0.12.1", "quickcheck", + "quinn", "quinn-proto", "rand 0.8.5", "rustls 0.20.8", @@ -3863,6 +3864,27 @@ dependencies = [ "pin-project-lite 0.1.12", ] +[[package]] +name = "quinn" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445cbfe2382fa023c4f2f3c7e1c95c03dcc1df2bf23cebcb2b13e1402c4394d1" +dependencies = [ + "async-io", + "async-std", + "bytes", + "futures-io", + "pin-project-lite 0.2.9", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.20.8", + "thiserror", + "tokio", + "tracing", + "webpki 0.22.0", +] + [[package]] name = "quinn-proto" version = "0.9.2" @@ -3874,6 +3896,7 @@ dependencies = [ "ring", "rustc-hash", "rustls 0.20.8", + "rustls-native-certs", "slab", "thiserror", "tinyvec", @@ -3881,6 +3904,19 @@ dependencies = [ "webpki 0.22.0", ] +[[package]] +name = "quinn-udp" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "641538578b21f5e5c8ea733b736895576d0fe329bb883b937db6f4d163dbaaf4" +dependencies = [ + "libc", + "quinn-proto", + "socket2", + "tracing", + "windows-sys", +] + [[package]] name = "quote" version = "1.0.23" @@ -4233,6 +4269,18 @@ dependencies = [ "webpki 0.22.0", ] +[[package]] +name = "rustls-native-certs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "1.0.2" diff --git a/transports/quic/Cargo.toml b/transports/quic/Cargo.toml index b059edceca6..8816ac057f7 100644 --- a/transports/quic/Cargo.toml +++ b/transports/quic/Cargo.toml @@ -19,6 +19,8 @@ libp2p-tls = { version = "0.1.0-alpha.1", path = "../tls" } log = "0.4" parking_lot = "0.12.0" quinn-proto = { version = "0.9.0", default-features = false, features = ["tls-rustls"] } +quinn = { version = "0.9.0", features = ["tls-rustls", "futures-io", "runtime-async-std"] } +#quinn = { version = "0.9.0", default-features = false, features = ["tls-rustls"] } rand = "0.8.5" rustls = { version = "0.20.2", default-features = false } thiserror = "1.0.26" diff --git a/transports/quic/src/connection.rs b/transports/quic/src/connection.rs index 0e5727dcf21..ad12efcb34e 100644 --- a/transports/quic/src/connection.rs +++ b/transports/quic/src/connection.rs @@ -22,27 +22,71 @@ mod connecting; mod substream; use crate::{ - endpoint::{self, ToEndpoint}, Error, }; pub use connecting::Connecting; pub use substream::Substream; -use substream::{SubstreamState, WriteState}; -use futures::{channel::mpsc, ready, FutureExt, StreamExt}; -use futures_timer::Delay; +use futures::{future::BoxFuture, FutureExt}; use libp2p_core::muxing::{StreamMuxer, StreamMuxerEvent}; -use parking_lot::Mutex; use std::{ - any::Any, - collections::HashMap, - net::SocketAddr, pin::Pin, - sync::Arc, - task::{Context, Poll, Waker}, - time::Instant, + task::{Context, Poll}, }; +/// State for a single opened QUIC connection. +pub struct Connection { + connection: quinn::Connection, + incoming: + BoxFuture<'static, Result<(quinn::SendStream, quinn::RecvStream), quinn::ConnectionError>>, + outgoing: + BoxFuture<'static, Result<(quinn::SendStream, quinn::RecvStream), quinn::ConnectionError>>, +} + +impl StreamMuxer for Connection { + type Substream = Substream; + type Error = quinn::ConnectionError; // TODO Error + + fn poll_inbound( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + let this = self.get_mut(); + + let (send, recv) = futures::ready!(this.incoming.poll_unpin(cx))?; + let connection = this.connection.clone(); + this.incoming = Box::pin(async move { connection.accept_bi().await }); + let substream = Substream::new(send, recv); + Poll::Ready(Ok(substream)) + } + + fn poll_outbound( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + let this = self.get_mut(); + + let (send, recv) = futures::ready!(this.outgoing.poll_unpin(cx))?; + let connection = this.connection.clone(); + this.outgoing = Box::pin(async move { connection.open_bi().await }); + let substream = Substream::new(send, recv); + Poll::Ready(Ok(substream)) + } + + fn poll( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Pending + } + + fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + self.connection.close(From::from(0u32), &[]); + Poll::Ready(Ok(())) + } +} + +/* /// State for a single opened QUIC connection. #[derive(Debug)] pub struct Connection { @@ -425,3 +469,5 @@ impl State { .expect("Substream should be known.") } } + +*/ diff --git a/transports/quic/src/connection/connecting.rs b/transports/quic/src/connection/connecting.rs index 3124885e772..1b80caa7b92 100644 --- a/transports/quic/src/connection/connecting.rs +++ b/transports/quic/src/connection/connecting.rs @@ -22,7 +22,7 @@ use crate::{Connection, Error}; -use futures::prelude::*; +use futures::{prelude::*, future::Either}; use futures_timer::Delay; use libp2p_core::PeerId; use std::{ @@ -34,64 +34,59 @@ use std::{ /// A QUIC connection currently being negotiated. #[derive(Debug)] pub struct Connecting { - connection: Option, - timeout: Delay, + connecting: futures::future::Select, + //timeout: Delay, } impl Connecting { - pub(crate) fn new(connection: Connection, timeout: Duration) -> Self { + pub(crate) fn new(connection: quinn::Connecting, timeout: Duration) -> Self { Connecting { - connection: Some(connection), - timeout: Delay::new(timeout), + connecting: futures::future::select(connection, Delay::new(timeout)), + //timeout: Delay::new(timeout), } } } +impl Connecting { + /// Returns the address of the node we're connected to. + /// Panics if the connection is still handshaking. + fn remote_peer_id(connection: &quinn::Connection) -> PeerId { + //debug_assert!(!connection.handshake_data().is_some()); + let identity = connection + .peer_identity() + .expect("connection got identity because it passed TLS handshake; qed"); + let certificates: Box> = + identity.downcast().expect("we rely on rustls feature; qed"); + let end_entity = certificates + .get(0) + .expect("there should be exactly one certificate; qed"); + let p2p_cert = libp2p_tls::certificate::parse(end_entity) + .expect("the certificate was validated during TLS handshake; qed"); + p2p_cert.peer_id() + } +} + impl Future for Connecting { type Output = Result<(PeerId, Connection), Error>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let connection = self - .connection - .as_mut() - .expect("Future polled after it has completed"); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let connecting = Pin::new(&mut self.get_mut().connecting); - loop { - let event = match connection.poll_event(cx) { - Poll::Ready(Some(event)) => event, - Poll::Ready(None) => return Poll::Ready(Err(Error::EndpointDriverCrashed)), - Poll::Pending => { - return self - .timeout - .poll_unpin(cx) - .map(|()| Err(Error::HandshakeTimedOut)); - } - }; - match event { - quinn_proto::Event::Connected => { - // Parse the remote's Id identity from the certificate. - let identity = connection - .peer_identity() - .expect("connection got identity because it passed TLS handshake; qed"); - let certificates: Box> = - identity.downcast().expect("we rely on rustls feature; qed"); - let end_entity = certificates - .get(0) - .expect("there should be exactly one certificate; qed"); - let p2p_cert = libp2p_tls::certificate::parse(end_entity) - .expect("the certificate was validated during TLS handshake; qed"); - let peer_id = p2p_cert.peer_id(); + let connection = match futures::ready!(connecting.poll(cx)) { + Either::Right(_) => return Poll::Ready(Err(Error::HandshakeTimedOut)), + Either::Left((connection, _)) => connection.map_err(|e| Error::Connection(e.into()))?, + }; - return Poll::Ready(Ok((peer_id, self.connection.take().unwrap()))); - } - quinn_proto::Event::ConnectionLost { reason } => { - return Poll::Ready(Err(Error::Connection(reason.into()))) - } - quinn_proto::Event::HandshakeDataReady | quinn_proto::Event::Stream(_) => {} - quinn_proto::Event::DatagramReceived => { - debug_assert!(false, "Datagrams are not supported") - } - } - } + let peer_id = Self::remote_peer_id(&connection); + let connection_c = connection.clone(); + let incoming = Box::pin(async move { connection_c.accept_bi().await }); + let connection_c = connection.clone(); + let outgoing = Box::pin(async move { connection_c.open_bi().await }); + let muxer = Connection { + connection, + incoming, + outgoing, + }; + Poll::Ready(Ok((peer_id, muxer))) } } diff --git a/transports/quic/src/connection/substream.rs b/transports/quic/src/connection/substream.rs index b3a82542e9c..97dc8cbca79 100644 --- a/transports/quic/src/connection/substream.rs +++ b/transports/quic/src/connection/substream.rs @@ -19,15 +19,64 @@ // DEALINGS IN THE SOFTWARE. use std::{ - io::{self, Write}, + io::{self}, pin::Pin, - sync::Arc, - task::{Context, Poll, Waker}, + task::{Context, Poll}, }; use futures::{AsyncRead, AsyncWrite}; -use parking_lot::Mutex; + +pub struct Substream { + send: quinn::SendStream, + recv: quinn::RecvStream, + closed: bool, +} + +impl Substream { + pub(super) fn new(send: quinn::SendStream, recv: quinn::RecvStream) -> Self { + Self { + send, + recv, + closed: false, + } + } +} + +impl AsyncRead for Substream { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context, + buf: &mut [u8], + ) -> Poll> { + AsyncRead::poll_read(Pin::new(&mut self.get_mut().recv), cx, buf) + } +} + +impl AsyncWrite for Substream { + fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll> { + AsyncWrite::poll_write(Pin::new(&mut self.get_mut().send), cx, buf) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + AsyncWrite::poll_flush(Pin::new(&mut self.get_mut().send), cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let this = self.get_mut(); + if this.closed { + // For some reason poll_close needs to be 'fuse'able + return Poll::Ready(Ok(())); + } + let close_result = AsyncWrite::poll_close(Pin::new(&mut this.send), cx); + if close_result.is_ready() { + this.closed = true; + } + close_result + } +} + +/* use super::State; /// Wakers for the [`AsyncRead`] and [`AsyncWrite`] on a substream. @@ -255,3 +304,5 @@ pub enum WriteState { /// sent. Stopped, } + +*/ \ No newline at end of file diff --git a/transports/quic/src/endpoint.rs b/transports/quic/src/endpoint.rs index 024062e379a..e23bac26334 100644 --- a/transports/quic/src/endpoint.rs +++ b/transports/quic/src/endpoint.rs @@ -113,9 +113,9 @@ impl Config { /// Represents the inner configuration for [`quinn_proto`]. #[derive(Debug, Clone)] pub struct QuinnConfig { - client_config: quinn_proto::ClientConfig, - server_config: Arc, - endpoint_config: Arc, + pub(crate) client_config: quinn_proto::ClientConfig, + pub(crate) server_config: Arc, + pub(crate) endpoint_config: Arc, } impl From for QuinnConfig { @@ -167,6 +167,7 @@ impl From for QuinnConfig { } } +/* /// Channel used to send commands to the [`Driver`]. #[derive(Debug, Clone)] pub struct Channel { @@ -287,11 +288,13 @@ impl Channel { let _ = self.to_endpoint.clone().try_send(to_endpoint); } } +*/ #[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)] #[error("Background task disconnected")] pub struct Disconnected {} +/* /// Message sent to the endpoint background task. #[derive(Debug)] pub enum ToEndpoint { @@ -666,3 +669,4 @@ impl Future for Driver

{ Poll::Ready(()) } } +*/ diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index 54c1fec9e30..21d4947ebd2 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::endpoint::{Config, QuinnConfig, ToEndpoint}; +use crate::endpoint::{Config, QuinnConfig}; use crate::provider::Provider; use crate::{endpoint, Connecting, Connection, Error}; @@ -74,6 +74,9 @@ pub struct GenTransport { waker: Option, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +struct Dialer; + impl GenTransport

{ /// Create a new [`GenTransport`] with the given [`Config`]. pub fn new(config: Config) -> Self { @@ -100,11 +103,13 @@ impl Transport for GenTransport

{ fn listen_on(&mut self, addr: Multiaddr) -> Result> { let (socket_addr, version) = multiaddr_to_socketaddr(&addr, self.support_draft_29) .ok_or(TransportError::MultiaddrNotSupported(addr))?; + let server_config = quinn::ServerConfig::clone(&self.quinn_config.server_config); + let endpoint = quinn::Endpoint::server(server_config, socket_addr).unwrap(); // TODO with runtime + version let listener_id = ListenerId::new(); let listener = Listener::new( listener_id, socket_addr, - self.quinn_config.clone(), + endpoint, self.handshake_timeout, version, )?; @@ -143,6 +148,8 @@ impl Transport for GenTransport

{ } fn dial(&mut self, addr: Multiaddr) -> Result> { + return Err(TransportError::MultiaddrNotSupported(addr)); + /* let (socket_addr, version) = multiaddr_to_socketaddr(&addr, self.support_draft_29) .ok_or_else(|| TransportError::MultiaddrNotSupported(addr.clone()))?; if socket_addr.port() == 0 || socket_addr.ip().is_unspecified() { @@ -188,6 +195,7 @@ impl Transport for GenTransport

{ } }; Ok(dialer_state.new_dial(socket_addr, self.handshake_timeout, version)) + */ } fn dial_as_listener( @@ -205,6 +213,7 @@ impl Transport for GenTransport

{ mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { + /* let mut errored = Vec::new(); for (key, dialer) in &mut self.dialer { if let Poll::Ready(_error) = dialer.poll(cx) { @@ -217,6 +226,7 @@ impl Transport for GenTransport

{ // Drop dialer and all pending dials so that the connection receiver is notified. self.dialer.remove(&key); } + */ if let Poll::Ready(Some(ev)) = self.listeners.poll_next_unpin(cx) { return Poll::Ready(ev); @@ -233,6 +243,7 @@ impl From for TransportError { } } +/* /// Dialer for addresses if no matching listener exists. #[derive(Debug)] struct Dialer { @@ -324,6 +335,7 @@ impl DialerState { Poll::Pending } } +*/ /// Listener for incoming connections. struct Listener { @@ -332,13 +344,15 @@ struct Listener { version: ProtocolVersion, - /// Channel to the endpoint to initiate dials. - endpoint_channel: endpoint::Channel, - /// Queued dials. - dialer_state: DialerState, + /// Endpoint + endpoint: quinn::Endpoint, + // /// Channel to the endpoint to initiate dials. + // endpoint_channel: endpoint::Channel, + // /// Queued dials. + // dialer_state: DialerState, - /// Channel where new connections are being sent. - new_connections_rx: mpsc::Receiver, + /// A future to poll new incoming connections. + accept: BoxFuture<'static, Option>, /// Timeout for connection establishment on inbound connections. handshake_timeout: Duration, @@ -361,12 +375,13 @@ impl Listener

{ fn new( listener_id: ListenerId, socket_addr: SocketAddr, - config: QuinnConfig, + endpoint: quinn::Endpoint, + // config: QuinnConfig, handshake_timeout: Duration, version: ProtocolVersion, ) -> Result { - let (endpoint_channel, new_connections_rx) = - endpoint::Channel::new_bidirectional::

(config, socket_addr)?; + // let (endpoint_channel, new_connections_rx) = + // endpoint::Channel::new_bidirectional::

(config, socket_addr)?; let if_watcher; let pending_event; @@ -375,23 +390,28 @@ impl Listener

{ pending_event = None; } else { if_watcher = None; - let ma = socketaddr_to_multiaddr(endpoint_channel.socket_addr(), version); + let ma = socketaddr_to_multiaddr(&socket_addr, version); pending_event = Some(TransportEvent::NewAddress { listener_id, listen_addr: ma, }) } + let endpoint_c = endpoint.clone(); + let accept = Box::pin(async move { endpoint_c.accept().await }); + Ok(Listener { - endpoint_channel, + endpoint, + accept, + // endpoint_channel, listener_id, version, - new_connections_rx, + // new_connections_rx, handshake_timeout, if_watcher, is_closed: false, pending_event, - dialer_state: DialerState::default(), + // dialer_state: DialerState::default(), close_listener_waker: None, }) } @@ -402,6 +422,7 @@ impl Listener

{ if self.is_closed { return; } + self.endpoint.close(From::from(0u32), &[]); self.pending_event = Some(TransportEvent::ListenerClosed { listener_id: self.listener_id, reason, @@ -414,8 +435,13 @@ impl Listener

{ } } + fn socket_addr(&self) -> SocketAddr { + self.endpoint.local_addr().unwrap() + } + /// Poll for a next If Event. fn poll_if_addr(&mut self, cx: &mut Context<'_>) -> Poll<::Item> { + let endpoint_addr = self.socket_addr(); let if_watcher = match self.if_watcher.as_mut() { Some(iw) => iw, None => return Poll::Pending, @@ -424,7 +450,7 @@ impl Listener

{ match ready!(P::poll_if_event(if_watcher, cx)) { Ok(IfEvent::Up(inet)) => { if let Some(listen_addr) = ip_to_listenaddr( - self.endpoint_channel.socket_addr(), + &endpoint_addr, inet.addr(), self.version, ) { @@ -437,7 +463,7 @@ impl Listener

{ } Ok(IfEvent::Down(inet)) => { if let Some(listen_addr) = ip_to_listenaddr( - self.endpoint_channel.socket_addr(), + &endpoint_addr, inet.addr(), self.version, ) { @@ -458,16 +484,16 @@ impl Listener

{ } } - /// Poll [`DialerState`] to initiate requested dials. - fn poll_dialer(&mut self, cx: &mut Context<'_>) -> Poll { - let Self { - dialer_state, - endpoint_channel, - .. - } = self; + // /// Poll [`DialerState`] to initiate requested dials. + // fn poll_dialer(&mut self, cx: &mut Context<'_>) -> Poll { + // let Self { + // dialer_state, + // endpoint_channel, + // .. + // } = self; - dialer_state.poll(endpoint_channel, cx) - } + // dialer_state.poll(endpoint_channel, cx) + // } } impl Stream for Listener

{ @@ -483,17 +509,21 @@ impl Stream for Listener

{ if let Poll::Ready(event) = self.poll_if_addr(cx) { return Poll::Ready(Some(event)); } - if let Poll::Ready(error) = self.poll_dialer(cx) { - self.close(Err(error)); - continue; - } - match self.new_connections_rx.poll_next_unpin(cx) { - Poll::Ready(Some(connection)) => { - let local_addr = socketaddr_to_multiaddr(connection.local_addr(), self.version); + // if let Poll::Ready(error) = self.poll_dialer(cx) { + // self.close(Err(error)); + // continue; + // } + + match self.accept.poll_unpin(cx) { + Poll::Ready(Some(connecting)) => { + let endpoint = self.endpoint.clone(); + self.accept = Box::pin(async move { endpoint.accept().await }); + + let local_addr = socketaddr_to_multiaddr(&self.socket_addr(), self.version); let send_back_addr = - socketaddr_to_multiaddr(&connection.remote_addr(), self.version); + socketaddr_to_multiaddr(&connecting.remote_address(), self.version); let event = TransportEvent::Incoming { - upgrade: Connecting::new(connection, self.handshake_timeout), + upgrade: Connecting::new(connecting, self.handshake_timeout), local_addr, send_back_addr, listener_id: self.listener_id, @@ -518,9 +548,9 @@ impl fmt::Debug for Listener

{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Listener") .field("listener_id", &self.listener_id) - .field("endpoint_channel", &self.endpoint_channel) - .field("dialer_state", &self.dialer_state) - .field("new_connections_rx", &self.new_connections_rx) + //.field("endpoint_channel", &self.endpoint_channel) + //.field("dialer_state", &self.dialer_state) + //.field("new_connections_rx", &self.new_connections_rx) .field("handshake_timeout", &self.handshake_timeout) .field("is_closed", &self.is_closed) .field("pending_event", &self.pending_event) @@ -528,12 +558,6 @@ impl fmt::Debug for Listener

{ } } -impl Drop for Listener

{ - fn drop(&mut self) { - self.endpoint_channel.send_on_drop(ToEndpoint::Decoupled); - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ProtocolVersion { V1, // i.e. RFC9000 @@ -827,55 +851,56 @@ mod test { } } - #[cfg(feature = "tokio")] - #[tokio::test] - async fn test_dialer_drop() { - let keypair = libp2p_core::identity::Keypair::generate_ed25519(); - let config = Config::new(&keypair); - let mut transport = crate::tokio::Transport::new(config); - - let _dial = transport - .dial("/ip4/123.45.67.8/udp/1234/quic-v1".parse().unwrap()) - .unwrap(); - - // Expect a dialer and its background task to exist. - let mut channel = transport - .dialer - .get(&SocketFamily::Ipv4) - .unwrap() - .endpoint_channel - .clone(); - assert!(!transport.dialer.contains_key(&SocketFamily::Ipv6)); - - // Send dummy dial to check that the endpoint driver is running. - poll_fn(|cx| { - let (tx, _) = oneshot::channel(); - let _ = channel - .try_send( - ToEndpoint::Dial { - addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0), - result: tx, - version: ProtocolVersion::V1, - }, - cx, - ) - .unwrap(); - Poll::Ready(()) - }) - .await; - - // Start listening so that the dialer and driver are dropped. - let _ = transport - .listen_on("/ip4/0.0.0.0/udp/0/quic-v1".parse().unwrap()) - .unwrap(); - assert!(!transport.dialer.contains_key(&SocketFamily::Ipv4)); - - // Check that the [`Driver`] has shut down. - Delay::new(Duration::from_millis(10)).await; - poll_fn(|cx| { - assert!(channel.try_send(ToEndpoint::Decoupled, cx).is_err()); - Poll::Ready(()) - }) - .await; - } + + // #[cfg(feature = "tokio")] + // #[tokio::test] + // async fn test_dialer_drop() { + // let keypair = libp2p_core::identity::Keypair::generate_ed25519(); + // let config = Config::new(&keypair); + // let mut transport = crate::tokio::Transport::new(config); + + // let _dial = transport + // .dial("/ip4/123.45.67.8/udp/1234/quic-v1".parse().unwrap()) + // .unwrap(); + + // // Expect a dialer and its background task to exist. + // let mut channel = transport + // .dialer + // .get(&SocketFamily::Ipv4) + // .unwrap() + // .endpoint_channel + // .clone(); + // assert!(!transport.dialer.contains_key(&SocketFamily::Ipv6)); + + // // Send dummy dial to check that the endpoint driver is running. + // poll_fn(|cx| { + // let (tx, _) = oneshot::channel(); + // let _ = channel + // .try_send( + // ToEndpoint::Dial { + // addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0), + // result: tx, + // version: ProtocolVersion::V1, + // }, + // cx, + // ) + // .unwrap(); + // Poll::Ready(()) + // }) + // .await; + + // // Start listening so that the dialer and driver are dropped. + // let _ = transport + // .listen_on("/ip4/0.0.0.0/udp/0/quic-v1".parse().unwrap()) + // .unwrap(); + // assert!(!transport.dialer.contains_key(&SocketFamily::Ipv4)); + + // // Check that the [`Driver`] has shut down. + // Delay::new(Duration::from_millis(10)).await; + // poll_fn(|cx| { + // assert!(channel.try_send(ToEndpoint::Decoupled, cx).is_err()); + // Poll::Ready(()) + // }) + // .await; + // } } From 8bce874b7bac0a81190e86b88f2e15e6eb761010 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 11 Feb 2023 02:56:42 +0000 Subject: [PATCH 02/60] Implement fn dial --- transports/quic/src/transport.rs | 42 ++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index 21d4947ebd2..df9c43204d7 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -39,7 +39,7 @@ use std::collections::hash_map::{DefaultHasher, Entry}; use std::collections::{HashMap, VecDeque}; use std::fmt; use std::hash::{Hash, Hasher}; -use std::net::IpAddr; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::time::Duration; use std::{ net::SocketAddr, @@ -69,14 +69,11 @@ pub struct GenTransport { /// Streams of active [`Listener`]s. listeners: SelectAll>, /// Dialer for each socket family if no matching listener exists. - dialer: HashMap, + dialer: HashMap, /// Waker to poll the transport again when a new dialer or listener is added. waker: Option, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -struct Dialer; - impl GenTransport

{ /// Create a new [`GenTransport`] with the given [`Config`]. pub fn new(config: Config) -> Self { @@ -148,54 +145,63 @@ impl Transport for GenTransport

{ } fn dial(&mut self, addr: Multiaddr) -> Result> { - return Err(TransportError::MultiaddrNotSupported(addr)); - /* let (socket_addr, version) = multiaddr_to_socketaddr(&addr, self.support_draft_29) .ok_or_else(|| TransportError::MultiaddrNotSupported(addr.clone()))?; if socket_addr.port() == 0 || socket_addr.ip().is_unspecified() { return Err(TransportError::MultiaddrNotSupported(addr)); } - let mut listeners = self + let listeners = self .listeners .iter_mut() .filter(|l| { if l.is_closed { return false; } - let listen_addr = l.endpoint_channel.socket_addr(); + let listen_addr = l.socket_addr(); SocketFamily::is_same(&listen_addr.ip(), &socket_addr.ip()) && listen_addr.ip().is_loopback() == socket_addr.ip().is_loopback() }) .collect::>(); - let dialer_state = match listeners.len() { + let endpoint = match listeners.len() { 0 => { // No listener. Get or create an explicit dialer. let socket_family = socket_addr.ip().into(); let dialer = match self.dialer.entry(socket_family) { - Entry::Occupied(occupied) => occupied.into_mut(), + Entry::Occupied(occupied) => occupied.get().clone(), Entry::Vacant(vacant) => { if let Some(waker) = self.waker.take() { waker.wake(); } - vacant.insert(Dialer::new::

(self.quinn_config.clone(), socket_family)?) + let listen_socket_addr = match socket_family { + SocketFamily::Ipv4 => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), + SocketFamily::Ipv6 => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), + }; + let server_config = quinn::ServerConfig::clone(&self.quinn_config.server_config); + let endpoint = quinn::Endpoint::server(server_config, listen_socket_addr).unwrap(); // TODO with runtime + version + + vacant.insert(endpoint.clone()); + endpoint } }; - &mut dialer.state + dialer } - 1 => &mut listeners[0].dialer_state, _ => { // Pick any listener to use for dialing. // We hash the socket address to achieve determinism. let mut hasher = DefaultHasher::new(); socket_addr.hash(&mut hasher); let index = hasher.finish() as usize % listeners.len(); - &mut listeners[index].dialer_state + listeners[index].endpoint.clone() } }; - Ok(dialer_state.new_dial(socket_addr, self.handshake_timeout, version)) - */ + let handshake_timeout = self.handshake_timeout; + let client_config = quinn::ClientConfig::clone(&self.quinn_config.client_config); + Ok(Box::pin(async move { + let connecting = endpoint.connect_with(client_config, socket_addr, "l").unwrap(); // TODO handle unwrap + Connecting::new(connecting, handshake_timeout).await + })) } fn dial_as_listener( @@ -390,7 +396,7 @@ impl Listener

{ pending_event = None; } else { if_watcher = None; - let ma = socketaddr_to_multiaddr(&socket_addr, version); + let ma = socketaddr_to_multiaddr(&endpoint.local_addr().unwrap(), version); // TODO handle unwrap pending_event = Some(TransportEvent::NewAddress { listener_id, listen_addr: ma, From 2fa340856c5db42f4172031bf05c8b2dd6ab8d08 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 11 Feb 2023 16:11:34 +0000 Subject: [PATCH 03/60] Handle quic version --- transports/quic/src/transport.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index df9c43204d7..8955486e5de 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -39,7 +39,7 @@ use std::collections::hash_map::{DefaultHasher, Entry}; use std::collections::{HashMap, VecDeque}; use std::fmt; use std::hash::{Hash, Hasher}; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, UdpSocket}; use std::time::Duration; use std::{ net::SocketAddr, @@ -89,6 +89,11 @@ impl GenTransport

{ support_draft_29, } } + fn new_endpoint(endpoint_config: quinn::EndpointConfig, server_config: Option, socket_addr: SocketAddr) -> Result { + let socket = UdpSocket::bind(socket_addr)?; + let endpoint = quinn::Endpoint::new(endpoint_config, server_config, socket, quinn::TokioRuntime)?; // TODO with runtime + Ok(endpoint) + } } impl Transport for GenTransport

{ @@ -100,8 +105,9 @@ impl Transport for GenTransport

{ fn listen_on(&mut self, addr: Multiaddr) -> Result> { let (socket_addr, version) = multiaddr_to_socketaddr(&addr, self.support_draft_29) .ok_or(TransportError::MultiaddrNotSupported(addr))?; + let endpoint_config = quinn::EndpointConfig::clone(&self.quinn_config.endpoint_config); let server_config = quinn::ServerConfig::clone(&self.quinn_config.server_config); - let endpoint = quinn::Endpoint::server(server_config, socket_addr).unwrap(); // TODO with runtime + version + let endpoint = Self::new_endpoint(endpoint_config, Some(server_config), socket_addr)?; let listener_id = ListenerId::new(); let listener = Listener::new( listener_id, @@ -178,8 +184,8 @@ impl Transport for GenTransport

{ SocketFamily::Ipv4 => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), SocketFamily::Ipv6 => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), }; - let server_config = quinn::ServerConfig::clone(&self.quinn_config.server_config); - let endpoint = quinn::Endpoint::server(server_config, listen_socket_addr).unwrap(); // TODO with runtime + version + let endpoint_config = quinn::EndpointConfig::clone(&self.quinn_config.endpoint_config); + let endpoint = Self::new_endpoint(endpoint_config, None, listen_socket_addr)?; vacant.insert(endpoint.clone()); endpoint @@ -197,8 +203,14 @@ impl Transport for GenTransport

{ } }; let handshake_timeout = self.handshake_timeout; - let client_config = quinn::ClientConfig::clone(&self.quinn_config.client_config); + let mut client_config = quinn::ClientConfig::clone(&self.quinn_config.client_config); + if version == ProtocolVersion::Draft29 { + client_config.version(0xff00_001d); + } Ok(Box::pin(async move { + // This `"l"` seems necessary because an empty string is an invalid domain + // name. While we don't use domain names, the underlying rustls library + // is based upon the assumption that we do. let connecting = endpoint.connect_with(client_config, socket_addr, "l").unwrap(); // TODO handle unwrap Connecting::new(connecting, handshake_timeout).await })) From 22b7944d86ea3439c6ca4f76f085f1a5654fee60 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 11 Feb 2023 16:32:23 +0000 Subject: [PATCH 04/60] Implement tokio/async-std switch --- Cargo.lock | 13 -- transports/quic/Cargo.toml | 7 +- transports/quic/src/provider.rs | 38 +++--- transports/quic/src/provider/async_std.rs | 7 ++ transports/quic/src/provider/tokio.rs | 7 ++ transports/quic/src/transport.rs | 138 +++++++++++----------- 6 files changed, 107 insertions(+), 103 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8db906e6e56..882e0432a76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3896,7 +3896,6 @@ dependencies = [ "ring", "rustc-hash", "rustls 0.20.8", - "rustls-native-certs", "slab", "thiserror", "tinyvec", @@ -4269,18 +4268,6 @@ dependencies = [ "webpki 0.22.0", ] -[[package]] -name = "rustls-native-certs" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" -dependencies = [ - "openssl-probe", - "rustls-pemfile", - "schannel", - "security-framework", -] - [[package]] name = "rustls-pemfile" version = "1.0.2" diff --git a/transports/quic/Cargo.toml b/transports/quic/Cargo.toml index 8816ac057f7..a9ddc5324c2 100644 --- a/transports/quic/Cargo.toml +++ b/transports/quic/Cargo.toml @@ -19,16 +19,15 @@ libp2p-tls = { version = "0.1.0-alpha.1", path = "../tls" } log = "0.4" parking_lot = "0.12.0" quinn-proto = { version = "0.9.0", default-features = false, features = ["tls-rustls"] } -quinn = { version = "0.9.0", features = ["tls-rustls", "futures-io", "runtime-async-std"] } -#quinn = { version = "0.9.0", default-features = false, features = ["tls-rustls"] } +quinn = { version = "0.9.0", default-features = false, features = ["tls-rustls", "futures-io"] } rand = "0.8.5" rustls = { version = "0.20.2", default-features = false } thiserror = "1.0.26" tokio = { version = "1.21.1", default-features = false, features = ["net", "rt"], optional = true } [features] -tokio = ["dep:tokio", "if-watch/tokio"] -async-std = ["dep:async-std", "if-watch/smol"] +tokio = ["dep:tokio", "if-watch/tokio", "quinn/runtime-tokio"] +async-std = ["dep:async-std", "if-watch/smol", "quinn/runtime-async-std"] # Passing arguments to the docsrs builder in order to properly document cfg's. # More information: https://docs.rs/about/builds#cross-compiling diff --git a/transports/quic/src/provider.rs b/transports/quic/src/provider.rs index c38f77fd1b9..6488d130560 100644 --- a/transports/quic/src/provider.rs +++ b/transports/quic/src/provider.rs @@ -39,27 +39,31 @@ const RECEIVE_BUFFER_SIZE: usize = 65536; /// and spawning tasks. pub trait Provider: Unpin + Send + Sized + 'static { type IfWatcher: Unpin + Send; + type Runtime: quinn::Runtime; - /// Create a new providing that is wrapping the socket. - /// - /// Note: The socket must be set to non-blocking. - fn from_socket(socket: std::net::UdpSocket) -> io::Result; + // /// Create a new providing that is wrapping the socket. + // /// + // /// Note: The socket must be set to non-blocking. + // fn from_socket(socket: std::net::UdpSocket) -> io::Result; - /// Receive a single packet. - /// - /// Returns the message and the address the message came from. - fn poll_recv_from(&mut self, cx: &mut Context<'_>) -> Poll, SocketAddr)>>; + // /// Receive a single packet. + // /// + // /// Returns the message and the address the message came from. + // fn poll_recv_from(&mut self, cx: &mut Context<'_>) -> Poll, SocketAddr)>>; - /// Set sending a packet on the socket. - /// - /// Since only one packet can be sent at a time, this may only be called if a preceding - /// call to [`Provider::poll_send_flush`] returned [`Poll::Ready`]. - fn start_send(&mut self, data: Vec, addr: SocketAddr); + // /// Set sending a packet on the socket. + // /// + // /// Since only one packet can be sent at a time, this may only be called if a preceding + // /// call to [`Provider::poll_send_flush`] returned [`Poll::Ready`]. + // fn start_send(&mut self, data: Vec, addr: SocketAddr); - /// Flush a packet send in [`Provider::start_send`]. - /// - /// If [`Poll::Ready`] is returned the socket is ready for sending a new packet. - fn poll_send_flush(&mut self, cx: &mut Context<'_>) -> Poll>; + // /// Flush a packet send in [`Provider::start_send`]. + // /// + // /// If [`Poll::Ready`] is returned the socket is ready for sending a new packet. + // fn poll_send_flush(&mut self, cx: &mut Context<'_>) -> Poll>; + + /// Run the corresponding runtime + fn runtime() -> Self::Runtime; /// Run the given future in the background until it ends. /// diff --git a/transports/quic/src/provider/async_std.rs b/transports/quic/src/provider/async_std.rs index 222c8e55e90..ab2267747a8 100644 --- a/transports/quic/src/provider/async_std.rs +++ b/transports/quic/src/provider/async_std.rs @@ -46,7 +46,9 @@ pub struct Provider { impl super::Provider for Provider { type IfWatcher = if_watch::smol::IfWatcher; + type Runtime = quinn::AsyncStdRuntime; + /* fn from_socket(socket: std::net::UdpSocket) -> io::Result { let socket = Arc::new(socket.into()); let recv_stream = ReceiveStream::new(Arc::clone(&socket)); @@ -89,6 +91,11 @@ impl super::Provider for Provider { Poll::Pending => Poll::Pending, } } + */ + + fn runtime() -> Self::Runtime { + quinn::AsyncStdRuntime + } fn spawn(future: impl Future + Send + 'static) { spawn(future); diff --git a/transports/quic/src/provider/tokio.rs b/transports/quic/src/provider/tokio.rs index 07e23f8813c..f90d50ecc46 100644 --- a/transports/quic/src/provider/tokio.rs +++ b/transports/quic/src/provider/tokio.rs @@ -41,7 +41,9 @@ pub struct Provider { impl super::Provider for Provider { type IfWatcher = if_watch::tokio::IfWatcher; + type Runtime = quinn::TokioRuntime; + /* fn from_socket(socket: std::net::UdpSocket) -> std::io::Result { let socket = UdpSocket::from_std(socket)?; Ok(Provider { @@ -80,6 +82,11 @@ impl super::Provider for Provider { fn start_send(&mut self, data: Vec, addr: SocketAddr) { self.next_packet_out = Some((data, addr)); } + */ + + fn runtime() -> Self::Runtime { + quinn::TokioRuntime + } fn spawn(future: impl Future + Send + 'static) { tokio::spawn(future); diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index 8955486e5de..5d197f839f1 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -91,7 +91,7 @@ impl GenTransport

{ } fn new_endpoint(endpoint_config: quinn::EndpointConfig, server_config: Option, socket_addr: SocketAddr) -> Result { let socket = UdpSocket::bind(socket_addr)?; - let endpoint = quinn::Endpoint::new(endpoint_config, server_config, socket, quinn::TokioRuntime)?; // TODO with runtime + let endpoint = quinn::Endpoint::new(endpoint_config, server_config, socket, P::runtime())?; Ok(endpoint) } } @@ -800,74 +800,74 @@ mod test { ); } - #[cfg(feature = "async-std")] - #[async_std::test] - async fn test_close_listener() { - let keypair = libp2p_core::identity::Keypair::generate_ed25519(); - let config = Config::new(&keypair); - let mut transport = crate::async_std::Transport::new(config); - assert!(poll_fn(|cx| Pin::new(&mut transport).as_mut().poll(cx)) - .now_or_never() - .is_none()); - - // Run test twice to check that there is no unexpected behaviour if `Transport.listener` - // is temporarily empty. - for _ in 0..2 { - let id = transport - .listen_on("/ip4/0.0.0.0/udp/0/quic-v1".parse().unwrap()) - .unwrap(); - - // Copy channel to use it later. - let mut channel = transport - .listeners - .iter() - .next() - .unwrap() - .endpoint_channel - .clone(); - - match poll_fn(|cx| Pin::new(&mut transport).as_mut().poll(cx)).await { - TransportEvent::NewAddress { - listener_id, - listen_addr, - } => { - assert_eq!(listener_id, id); - assert!( - matches!(listen_addr.iter().next(), Some(Protocol::Ip4(a)) if !a.is_unspecified()) - ); - assert!( - matches!(listen_addr.iter().nth(1), Some(Protocol::Udp(port)) if port != 0) - ); - assert!(matches!(listen_addr.iter().nth(2), Some(Protocol::QuicV1))); - } - e => panic!("Unexpected event: {e:?}"), - } - assert!(transport.remove_listener(id), "Expect listener to exist."); - match poll_fn(|cx| Pin::new(&mut transport).as_mut().poll(cx)).await { - TransportEvent::ListenerClosed { - listener_id, - reason: Ok(()), - } => { - assert_eq!(listener_id, id); - } - e => panic!("Unexpected event: {e:?}"), - } - // Poll once again so that the listener has the chance to return `Poll::Ready(None)` and - // be removed from the list of listeners. - assert!(poll_fn(|cx| Pin::new(&mut transport).as_mut().poll(cx)) - .now_or_never() - .is_none()); - assert!(transport.listeners.is_empty()); - - // Check that the [`Driver`] has shut down. - Delay::new(Duration::from_millis(10)).await; - poll_fn(|cx| { - assert!(channel.try_send(ToEndpoint::Decoupled, cx).is_err()); - Poll::Ready(()) - }) - .await; - } - } + // #[cfg(feature = "async-std")] + // #[async_std::test] + // async fn test_close_listener() { + // let keypair = libp2p_core::identity::Keypair::generate_ed25519(); + // let config = Config::new(&keypair); + // let mut transport = crate::async_std::Transport::new(config); + // assert!(poll_fn(|cx| Pin::new(&mut transport).as_mut().poll(cx)) + // .now_or_never() + // .is_none()); + + // // Run test twice to check that there is no unexpected behaviour if `Transport.listener` + // // is temporarily empty. + // for _ in 0..2 { + // let id = transport + // .listen_on("/ip4/0.0.0.0/udp/0/quic-v1".parse().unwrap()) + // .unwrap(); + + // // Copy channel to use it later. + // let mut channel = transport + // .listeners + // .iter() + // .next() + // .unwrap() + // .endpoint_channel + // .clone(); + + // match poll_fn(|cx| Pin::new(&mut transport).as_mut().poll(cx)).await { + // TransportEvent::NewAddress { + // listener_id, + // listen_addr, + // } => { + // assert_eq!(listener_id, id); + // assert!( + // matches!(listen_addr.iter().next(), Some(Protocol::Ip4(a)) if !a.is_unspecified()) + // ); + // assert!( + // matches!(listen_addr.iter().nth(1), Some(Protocol::Udp(port)) if port != 0) + // ); + // assert!(matches!(listen_addr.iter().nth(2), Some(Protocol::QuicV1))); + // } + // e => panic!("Unexpected event: {e:?}"), + // } + // assert!(transport.remove_listener(id), "Expect listener to exist."); + // match poll_fn(|cx| Pin::new(&mut transport).as_mut().poll(cx)).await { + // TransportEvent::ListenerClosed { + // listener_id, + // reason: Ok(()), + // } => { + // assert_eq!(listener_id, id); + // } + // e => panic!("Unexpected event: {e:?}"), + // } + // // Poll once again so that the listener has the chance to return `Poll::Ready(None)` and + // // be removed from the list of listeners. + // assert!(poll_fn(|cx| Pin::new(&mut transport).as_mut().poll(cx)) + // .now_or_never() + // .is_none()); + // assert!(transport.listeners.is_empty()); + + // // Check that the [`Driver`] has shut down. + // Delay::new(Duration::from_millis(10)).await; + // poll_fn(|cx| { + // assert!(channel.try_send(ToEndpoint::Decoupled, cx).is_err()); + // Poll::Ready(()) + // }) + // .await; + // } + // } // #[cfg(feature = "tokio")] From 3c77392d4fea6a8edb7a1e947f9c71f85808010a Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 11 Feb 2023 22:15:15 +0000 Subject: [PATCH 05/60] Fix one error handling --- transports/quic/src/connection/connecting.rs | 2 +- transports/quic/src/transport.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/transports/quic/src/connection/connecting.rs b/transports/quic/src/connection/connecting.rs index 1b80caa7b92..3a8d61e4563 100644 --- a/transports/quic/src/connection/connecting.rs +++ b/transports/quic/src/connection/connecting.rs @@ -74,7 +74,7 @@ impl Future for Connecting { let connection = match futures::ready!(connecting.poll(cx)) { Either::Right(_) => return Poll::Ready(Err(Error::HandshakeTimedOut)), - Either::Left((connection, _)) => connection.map_err(|e| Error::Connection(e.into()))?, + Either::Left((connection, _)) => connection.map_err(crate::ConnectionError)?, }; let peer_id = Self::remote_peer_id(&connection); diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index 5d197f839f1..afb1ea2f294 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -211,7 +211,8 @@ impl Transport for GenTransport

{ // This `"l"` seems necessary because an empty string is an invalid domain // name. While we don't use domain names, the underlying rustls library // is based upon the assumption that we do. - let connecting = endpoint.connect_with(client_config, socket_addr, "l").unwrap(); // TODO handle unwrap + let connecting = endpoint.connect_with(client_config, socket_addr, "l") + .map_err(crate::ConnectError)?; Connecting::new(connecting, handshake_timeout).await })) } From 729ebf9280fb4563faf1713156389ee2c3c87fff Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 11 Feb 2023 22:16:34 +0000 Subject: [PATCH 06/60] Revisit todo --- transports/quic/src/connection.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transports/quic/src/connection.rs b/transports/quic/src/connection.rs index ad12efcb34e..dde7992ca65 100644 --- a/transports/quic/src/connection.rs +++ b/transports/quic/src/connection.rs @@ -45,7 +45,7 @@ pub struct Connection { impl StreamMuxer for Connection { type Substream = Substream; - type Error = quinn::ConnectionError; // TODO Error + type Error = quinn::ConnectionError; fn poll_inbound( self: Pin<&mut Self>, From ff2dc4b2ccc29d36ed7a6815ad6a442a82cb4f9a Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 11 Feb 2023 22:25:58 +0000 Subject: [PATCH 07/60] Fix one error handling --- transports/quic/src/transport.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index afb1ea2f294..9eb160fb351 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -409,7 +409,7 @@ impl Listener

{ pending_event = None; } else { if_watcher = None; - let ma = socketaddr_to_multiaddr(&endpoint.local_addr().unwrap(), version); // TODO handle unwrap + let ma = socketaddr_to_multiaddr(&endpoint.local_addr()?, version); pending_event = Some(TransportEvent::NewAddress { listener_id, listen_addr: ma, From a288c972b1c72abaaf7c7142e03c22b9b12a582d Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 11 Feb 2023 22:36:24 +0000 Subject: [PATCH 08/60] Reduce the number of warnings --- transports/quic/src/connection.rs | 3 --- transports/quic/src/connection/connecting.rs | 4 ++-- transports/quic/src/endpoint.rs | 18 +----------------- transports/quic/src/provider.rs | 1 - transports/quic/src/provider/async_std.rs | 19 ++++++------------- transports/quic/src/provider/tokio.rs | 11 +++-------- transports/quic/src/transport.rs | 7 +++---- 7 files changed, 15 insertions(+), 48 deletions(-) diff --git a/transports/quic/src/connection.rs b/transports/quic/src/connection.rs index dde7992ca65..8fbb43e8767 100644 --- a/transports/quic/src/connection.rs +++ b/transports/quic/src/connection.rs @@ -21,9 +21,6 @@ mod connecting; mod substream; -use crate::{ - Error, -}; pub use connecting::Connecting; pub use substream::Substream; diff --git a/transports/quic/src/connection/connecting.rs b/transports/quic/src/connection/connecting.rs index 3a8d61e4563..136025920e1 100644 --- a/transports/quic/src/connection/connecting.rs +++ b/transports/quic/src/connection/connecting.rs @@ -20,7 +20,7 @@ //! Future that drives a QUIC connection until is has performed its TLS handshake. -use crate::{Connection, Error}; +use crate::{Connection, ConnectionError, Error}; use futures::{prelude::*, future::Either}; use futures_timer::Delay; @@ -74,7 +74,7 @@ impl Future for Connecting { let connection = match futures::ready!(connecting.poll(cx)) { Either::Right(_) => return Poll::Ready(Err(Error::HandshakeTimedOut)), - Either::Left((connection, _)) => connection.map_err(crate::ConnectionError)?, + Either::Left((connection, _)) => connection.map_err(ConnectionError)?, }; let peer_id = Self::remote_peer_id(&connection); diff --git a/transports/quic/src/endpoint.rs b/transports/quic/src/endpoint.rs index e23bac26334..41f891684d4 100644 --- a/transports/quic/src/endpoint.rs +++ b/transports/quic/src/endpoint.rs @@ -18,26 +18,10 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::{ - provider::Provider, - transport::{ProtocolVersion, SocketFamily}, - ConnectError, Connection, Error, -}; - -use bytes::BytesMut; -use futures::{ - channel::{mpsc, oneshot}, - prelude::*, -}; use quinn_proto::VarInt; use std::{ - collections::HashMap, - net::{Ipv4Addr, Ipv6Addr, SocketAddr}, - ops::ControlFlow, - pin::Pin, sync::Arc, - task::{Context, Poll}, - time::{Duration, Instant}, + time::Duration, }; // The `Driver` drops packets if the channel to the connection diff --git a/transports/quic/src/provider.rs b/transports/quic/src/provider.rs index 6488d130560..3f4acf572f1 100644 --- a/transports/quic/src/provider.rs +++ b/transports/quic/src/provider.rs @@ -22,7 +22,6 @@ use futures::Future; use if_watch::IfEvent; use std::{ io, - net::SocketAddr, task::{Context, Poll}, }; diff --git a/transports/quic/src/provider/async_std.rs b/transports/quic/src/provider/async_std.rs index ab2267747a8..b748d3af92a 100644 --- a/transports/quic/src/provider/async_std.rs +++ b/transports/quic/src/provider/async_std.rs @@ -18,13 +18,10 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use async_std::{net::UdpSocket, task::spawn}; -use futures::{future::BoxFuture, ready, Future, FutureExt, Stream, StreamExt}; +use async_std::task::spawn; +use futures::Future; use std::{ io, - net::SocketAddr, - pin::Pin, - sync::Arc, task::{Context, Poll}, }; @@ -33,16 +30,10 @@ use crate::GenTransport; /// Transport with [`async-std`] runtime. pub type Transport = GenTransport; +// TODO docs /// Provider for reading / writing to a sockets and spawning /// tasks using [`async-std`]. -pub struct Provider { - socket: Arc, - // Future for sending a packet. - // This is needed since [`async_Std::net::UdpSocket`] does not - // provide a poll-style interface for sending a packet. - send_packet: Option>>, - recv_stream: ReceiveStream, -} +pub struct Provider; impl super::Provider for Provider { type IfWatcher = if_watch::smol::IfWatcher; @@ -113,6 +104,7 @@ impl super::Provider for Provider { } } +/* type ReceiveStreamItem = ( Result<(usize, SocketAddr), io::Error>, Arc, @@ -156,3 +148,4 @@ impl Stream for ReceiveStream { Poll::Ready(Some(result)) } } +*/ diff --git a/transports/quic/src/provider/tokio.rs b/transports/quic/src/provider/tokio.rs index f90d50ecc46..1d0416e78cf 100644 --- a/transports/quic/src/provider/tokio.rs +++ b/transports/quic/src/provider/tokio.rs @@ -18,26 +18,21 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use futures::{ready, Future}; +use futures::Future; use std::{ io, - net::SocketAddr, task::{Context, Poll}, }; -use tokio::{io::ReadBuf, net::UdpSocket}; use crate::GenTransport; /// Transport with [`tokio`] runtime. pub type Transport = GenTransport; +// TODO docs /// Provider for reading / writing to a sockets and spawning /// tasks using [`tokio`]. -pub struct Provider { - socket: UdpSocket, - socket_recv_buffer: Vec, - next_packet_out: Option<(Vec, SocketAddr)>, -} +pub struct Provider; impl super::Provider for Provider { type IfWatcher = if_watch::tokio::IfWatcher; diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index 9eb160fb351..f5f7a5498bc 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -20,9 +20,8 @@ use crate::endpoint::{Config, QuinnConfig}; use crate::provider::Provider; -use crate::{endpoint, Connecting, Connection, Error}; +use crate::{Connecting, ConnectError, Connection, Error}; -use futures::channel::{mpsc, oneshot}; use futures::future::BoxFuture; use futures::ready; use futures::stream::StreamExt; @@ -36,7 +35,7 @@ use libp2p_core::{ PeerId, Transport, }; use std::collections::hash_map::{DefaultHasher, Entry}; -use std::collections::{HashMap, VecDeque}; +use std::collections::HashMap; use std::fmt; use std::hash::{Hash, Hasher}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, UdpSocket}; @@ -212,7 +211,7 @@ impl Transport for GenTransport

{ // name. While we don't use domain names, the underlying rustls library // is based upon the assumption that we do. let connecting = endpoint.connect_with(client_config, socket_addr, "l") - .map_err(crate::ConnectError)?; + .map_err(ConnectError)?; Connecting::new(connecting, handshake_timeout).await })) } From a5493a37d656915e0e7571e38b2cf8e2996f8180 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Sat, 11 Feb 2023 22:46:29 +0000 Subject: [PATCH 09/60] Remove a lot of commented code --- transports/quic/src/connection.rs | 386 --------------- transports/quic/src/connection/substream.rs | 232 --------- transports/quic/src/endpoint.rs | 500 -------------------- transports/quic/src/provider.rs | 21 - transports/quic/src/provider/async_std.rs | 91 ---- transports/quic/src/provider/tokio.rs | 41 -- transports/quic/src/transport.rs | 94 ---- 7 files changed, 1365 deletions(-) diff --git a/transports/quic/src/connection.rs b/transports/quic/src/connection.rs index 8fbb43e8767..88ed12d16ec 100644 --- a/transports/quic/src/connection.rs +++ b/transports/quic/src/connection.rs @@ -82,389 +82,3 @@ impl StreamMuxer for Connection { Poll::Ready(Ok(())) } } - -/* -/// State for a single opened QUIC connection. -#[derive(Debug)] -pub struct Connection { - /// State shared with the substreams. - state: Arc>, - /// Channel to the [`endpoint::Driver`] that drives the [`quinn_proto::Endpoint`] that - /// this connection belongs to. - endpoint_channel: endpoint::Channel, - /// Pending message to be sent to the [`quinn_proto::Endpoint`] in the [`endpoint::Driver`]. - pending_to_endpoint: Option, - /// Events that the [`quinn_proto::Endpoint`] will send in destination to our local - /// [`quinn_proto::Connection`]. - from_endpoint: mpsc::Receiver, - /// Identifier for this connection according to the [`quinn_proto::Endpoint`]. - /// Used when sending messages to the endpoint. - connection_id: quinn_proto::ConnectionHandle, - /// `Future` that triggers at the [`Instant`] that [`quinn_proto::Connection::poll_timeout`] - /// indicates. - next_timeout: Option<(Delay, Instant)>, -} - -impl Connection { - /// Build a [`Connection`] from raw components. - /// - /// This function assumes that there exists a [`Driver`](super::endpoint::Driver) - /// that will process the messages sent to `EndpointChannel::to_endpoint` and send us messages - /// on `from_endpoint`. - /// - /// `connection_id` is used to identify the local connection in the messages sent to - /// `to_endpoint`. - /// - /// This function assumes that the [`quinn_proto::Connection`] is completely fresh and none of - /// its methods has ever been called. Failure to comply might lead to logic errors and panics. - pub(crate) fn from_quinn_connection( - endpoint_channel: endpoint::Channel, - connection: quinn_proto::Connection, - connection_id: quinn_proto::ConnectionHandle, - from_endpoint: mpsc::Receiver, - ) -> Self { - let state = State { - connection, - substreams: HashMap::new(), - poll_connection_waker: None, - poll_inbound_waker: None, - poll_outbound_waker: None, - }; - Self { - endpoint_channel, - pending_to_endpoint: None, - next_timeout: None, - from_endpoint, - connection_id, - state: Arc::new(Mutex::new(state)), - } - } - - /// The address that the local socket is bound to. - pub(crate) fn local_addr(&self) -> &SocketAddr { - self.endpoint_channel.socket_addr() - } - - /// Returns the address of the node we're connected to. - pub(crate) fn remote_addr(&self) -> SocketAddr { - self.state.lock().connection.remote_address() - } - - /// Identity of the remote peer inferred from the handshake. - /// - /// `None` if the handshake is not complete yet, i.e. [`Self::poll_event`] - /// has not yet reported a [`quinn_proto::Event::Connected`] - fn peer_identity(&self) -> Option> { - self.state - .lock() - .connection - .crypto_session() - .peer_identity() - } - - /// Polls the connection for an event that happened on it. - /// - /// `quinn::proto::Connection` is polled in the order instructed in their docs: - /// 1. [`quinn_proto::Connection::poll_transmit`] - /// 2. [`quinn_proto::Connection::poll_timeout`] - /// 3. [`quinn_proto::Connection::poll_endpoint_events`] - /// 4. [`quinn_proto::Connection::poll`] - fn poll_event(&mut self, cx: &mut Context<'_>) -> Poll> { - let mut inner = self.state.lock(); - loop { - // Sending the pending event to the endpoint. If the endpoint is too busy, we just - // stop the processing here. - // We don't deliver substream-related events to the user as long as - // `to_endpoint` is full. This should propagate the back-pressure of `to_endpoint` - // being full to the user. - if let Some(to_endpoint) = self.pending_to_endpoint.take() { - match self.endpoint_channel.try_send(to_endpoint, cx) { - Ok(Ok(())) => {} - Ok(Err(to_endpoint)) => { - self.pending_to_endpoint = Some(to_endpoint); - return Poll::Pending; - } - Err(endpoint::Disconnected {}) => { - return Poll::Ready(None); - } - } - } - - match self.from_endpoint.poll_next_unpin(cx) { - Poll::Ready(Some(event)) => { - inner.connection.handle_event(event); - continue; - } - Poll::Ready(None) => { - return Poll::Ready(None); - } - Poll::Pending => {} - } - - // The maximum amount of segments which can be transmitted in a single Transmit - // if a platform supports Generic Send Offload (GSO). - // Set to 1 for now since not all platforms support GSO. - // TODO: Fix for platforms that support GSO. - let max_datagrams = 1; - // Poll the connection for packets to send on the UDP socket and try to send them on - // `to_endpoint`. - if let Some(transmit) = inner - .connection - .poll_transmit(Instant::now(), max_datagrams) - { - // TODO: ECN bits not handled - self.pending_to_endpoint = Some(ToEndpoint::SendUdpPacket(transmit)); - continue; - } - - match inner.connection.poll_timeout() { - Some(timeout) => match self.next_timeout { - Some((_, when)) if when == timeout => {} - _ => { - let now = Instant::now(); - // 0ns if now > when - let duration = timeout.duration_since(now); - let next_timeout = Delay::new(duration); - self.next_timeout = Some((next_timeout, timeout)) - } - }, - None => self.next_timeout = None, - } - - if let Some((timeout, when)) = self.next_timeout.as_mut() { - if timeout.poll_unpin(cx).is_ready() { - inner.connection.handle_timeout(*when); - continue; - } - } - - // The connection also needs to be able to send control messages to the endpoint. This is - // handled here, and we try to send them on `to_endpoint` as well. - if let Some(event) = inner.connection.poll_endpoint_events() { - let connection_id = self.connection_id; - self.pending_to_endpoint = Some(ToEndpoint::ProcessConnectionEvent { - connection_id, - event, - }); - continue; - } - - // The final step consists in returning the events related to the various substreams. - if let Some(ev) = inner.connection.poll() { - return Poll::Ready(Some(ev)); - } - - return Poll::Pending; - } - } -} - -impl StreamMuxer for Connection { - type Substream = Substream; - type Error = Error; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - while let Poll::Ready(event) = self.poll_event(cx) { - let mut inner = self.state.lock(); - let event = match event { - Some(event) => event, - None => return Poll::Ready(Err(Error::EndpointDriverCrashed)), - }; - match event { - quinn_proto::Event::Connected | quinn_proto::Event::HandshakeDataReady => { - debug_assert!( - false, - "Unexpected event {event:?} on established QUIC connection" - ); - } - quinn_proto::Event::ConnectionLost { reason } => { - inner - .connection - .close(Instant::now(), From::from(0u32), Default::default()); - inner.substreams.values_mut().for_each(|s| s.wake_all()); - return Poll::Ready(Err(Error::Connection(reason.into()))); - } - quinn_proto::Event::Stream(quinn_proto::StreamEvent::Opened { - dir: quinn_proto::Dir::Bi, - }) => { - if let Some(waker) = inner.poll_outbound_waker.take() { - waker.wake(); - } - } - quinn_proto::Event::Stream(quinn_proto::StreamEvent::Available { - dir: quinn_proto::Dir::Bi, - }) => { - if let Some(waker) = inner.poll_inbound_waker.take() { - waker.wake(); - } - } - quinn_proto::Event::Stream(quinn_proto::StreamEvent::Readable { id }) => { - if let Some(substream) = inner.substreams.get_mut(&id) { - if let Some(waker) = substream.read_waker.take() { - waker.wake(); - } - } - } - quinn_proto::Event::Stream(quinn_proto::StreamEvent::Writable { id }) => { - if let Some(substream) = inner.substreams.get_mut(&id) { - if let Some(waker) = substream.write_waker.take() { - waker.wake(); - } - } - } - quinn_proto::Event::Stream(quinn_proto::StreamEvent::Finished { id }) => { - if let Some(substream) = inner.substreams.get_mut(&id) { - if matches!( - substream.write_state, - WriteState::Open | WriteState::Closing - ) { - substream.write_state = WriteState::Closed; - } - if let Some(waker) = substream.write_waker.take() { - waker.wake(); - } - if let Some(waker) = substream.close_waker.take() { - waker.wake(); - } - } - } - quinn_proto::Event::Stream(quinn_proto::StreamEvent::Stopped { - id, - error_code: _, - }) => { - if let Some(substream) = inner.substreams.get_mut(&id) { - substream.write_state = WriteState::Stopped; - if let Some(waker) = substream.write_waker.take() { - waker.wake(); - } - if let Some(waker) = substream.close_waker.take() { - waker.wake(); - } - } - } - quinn_proto::Event::DatagramReceived - | quinn_proto::Event::Stream(quinn_proto::StreamEvent::Available { - dir: quinn_proto::Dir::Uni, - }) - | quinn_proto::Event::Stream(quinn_proto::StreamEvent::Opened { - dir: quinn_proto::Dir::Uni, - }) => { - unreachable!("We don't use datagrams or unidirectional streams.") - } - } - } - // TODO: If connection migration is enabled (currently disabled) address - // change on the connection needs to be handled. - - self.state.lock().poll_connection_waker = Some(cx.waker().clone()); - Poll::Pending - } - - fn poll_inbound( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - let mut inner = self.state.lock(); - - let substream_id = match inner.connection.streams().accept(quinn_proto::Dir::Bi) { - Some(id) => { - inner.poll_inbound_waker = None; - id - } - None => { - inner.poll_inbound_waker = Some(cx.waker().clone()); - return Poll::Pending; - } - }; - inner.substreams.insert(substream_id, Default::default()); - let substream = Substream::new(substream_id, self.state.clone()); - - Poll::Ready(Ok(substream)) - } - - fn poll_outbound( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - let mut inner = self.state.lock(); - let substream_id = match inner.connection.streams().open(quinn_proto::Dir::Bi) { - Some(id) => { - inner.poll_outbound_waker = None; - id - } - None => { - inner.poll_outbound_waker = Some(cx.waker().clone()); - return Poll::Pending; - } - }; - inner.substreams.insert(substream_id, Default::default()); - let substream = Substream::new(substream_id, self.state.clone()); - Poll::Ready(Ok(substream)) - } - - fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let mut inner = self.state.lock(); - if inner.connection.is_drained() { - return Poll::Ready(Ok(())); - } - - for substream in inner.substreams.keys().cloned().collect::>() { - let _ = inner.connection.send_stream(substream).finish(); - } - - if inner.connection.streams().send_streams() == 0 && !inner.connection.is_closed() { - inner - .connection - .close(Instant::now(), From::from(0u32), Default::default()) - } - drop(inner); - - loop { - match ready!(self.poll_event(cx)) { - Some(quinn_proto::Event::ConnectionLost { .. }) => return Poll::Ready(Ok(())), - None => return Poll::Ready(Err(Error::EndpointDriverCrashed)), - _ => {} - } - } - } -} - -impl Drop for Connection { - fn drop(&mut self) { - let to_endpoint = ToEndpoint::ProcessConnectionEvent { - connection_id: self.connection_id, - event: quinn_proto::EndpointEvent::drained(), - }; - self.endpoint_channel.send_on_drop(to_endpoint); - } -} - -/// Mutex-protected state of [`Connection`]. -#[derive(Debug)] -pub struct State { - /// The QUIC inner state machine for this specific connection. - connection: quinn_proto::Connection, - - /// State of all the substreams that the muxer reports as open. - pub substreams: HashMap, - - /// Waker to wake if a new outbound substream is opened. - pub poll_outbound_waker: Option, - /// Waker to wake if a new inbound substream was happened. - pub poll_inbound_waker: Option, - /// Waker to wake if the connection should be polled again. - pub poll_connection_waker: Option, -} - -impl State { - fn unchecked_substream_state(&mut self, id: quinn_proto::StreamId) -> &mut SubstreamState { - self.substreams - .get_mut(&id) - .expect("Substream should be known.") - } -} - -*/ diff --git a/transports/quic/src/connection/substream.rs b/transports/quic/src/connection/substream.rs index 97dc8cbca79..6dc101cdcf4 100644 --- a/transports/quic/src/connection/substream.rs +++ b/transports/quic/src/connection/substream.rs @@ -26,7 +26,6 @@ use std::{ use futures::{AsyncRead, AsyncWrite}; - pub struct Substream { send: quinn::SendStream, recv: quinn::RecvStream, @@ -75,234 +74,3 @@ impl AsyncWrite for Substream { close_result } } - -/* -use super::State; - -/// Wakers for the [`AsyncRead`] and [`AsyncWrite`] on a substream. -#[derive(Debug, Default, Clone)] -pub struct SubstreamState { - /// Waker to wake if the substream becomes readable. - pub read_waker: Option, - /// Waker to wake if the substream becomes writable, closed or stopped. - pub write_waker: Option, - /// Waker to wake if the substream becomes closed or stopped. - pub close_waker: Option, - - pub write_state: WriteState, -} - -impl SubstreamState { - /// Wake all wakers for reading, writing and closed the stream. - pub fn wake_all(&mut self) { - if let Some(waker) = self.read_waker.take() { - waker.wake(); - } - if let Some(waker) = self.write_waker.take() { - waker.wake(); - } - if let Some(waker) = self.close_waker.take() { - waker.wake(); - } - } -} - -/// A single stream on a connection -#[derive(Debug)] -pub struct Substream { - /// The id of the stream. - id: quinn_proto::StreamId, - /// The state of the [`super::Connection`] this stream belongs to. - state: Arc>, -} - -impl Substream { - pub fn new(id: quinn_proto::StreamId, state: Arc>) -> Self { - Self { id, state } - } -} - -impl AsyncRead for Substream { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - mut buf: &mut [u8], - ) -> Poll> { - let mut state = self.state.lock(); - - let mut stream = state.connection.recv_stream(self.id); - let mut chunks = match stream.read(true) { - Ok(chunks) => chunks, - Err(quinn_proto::ReadableError::UnknownStream) => { - return Poll::Ready(Ok(0)); - } - Err(quinn_proto::ReadableError::IllegalOrderedRead) => { - unreachable!( - "Illegal ordered read can only happen if `stream.read(false)` is used." - ); - } - }; - - let mut bytes = 0; - let mut pending = false; - let mut error = None; - loop { - if buf.is_empty() { - // Chunks::next will continue returning `Ok(Some(_))` with an - // empty chunk if there is no space left in the buffer, so we - // break early here. - break; - } - let chunk = match chunks.next(buf.len()) { - Ok(Some(chunk)) => chunk, - Ok(None) => break, - Err(err @ quinn_proto::ReadError::Reset(_)) => { - error = Some(Err(io::Error::new(io::ErrorKind::ConnectionReset, err))); - break; - } - Err(quinn_proto::ReadError::Blocked) => { - pending = true; - break; - } - }; - - buf.write_all(&chunk.bytes).expect("enough buffer space"); - bytes += chunk.bytes.len(); - } - if chunks.finalize().should_transmit() { - if let Some(waker) = state.poll_connection_waker.take() { - waker.wake(); - } - } - if let Some(err) = error { - return Poll::Ready(err); - } - - if pending && bytes == 0 { - let substream_state = state.unchecked_substream_state(self.id); - substream_state.read_waker = Some(cx.waker().clone()); - return Poll::Pending; - } - - Poll::Ready(Ok(bytes)) - } -} - -impl AsyncWrite for Substream { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - let mut state = self.state.lock(); - - match state.connection.send_stream(self.id).write(buf) { - Ok(bytes) => { - if let Some(waker) = state.poll_connection_waker.take() { - waker.wake(); - } - Poll::Ready(Ok(bytes)) - } - Err(quinn_proto::WriteError::Blocked) => { - let substream_state = state.unchecked_substream_state(self.id); - substream_state.write_waker = Some(cx.waker().clone()); - Poll::Pending - } - Err(err @ quinn_proto::WriteError::Stopped(_)) => { - Poll::Ready(Err(io::Error::new(io::ErrorKind::ConnectionReset, err))) - } - Err(quinn_proto::WriteError::UnknownStream) => { - Poll::Ready(Err(io::ErrorKind::BrokenPipe.into())) - } - } - } - - fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - // quinn doesn't support flushing, calling close will flush all substreams. - Poll::Ready(Ok(())) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let mut inner = self.state.lock(); - - let substream_state = inner.unchecked_substream_state(self.id); - match substream_state.write_state { - WriteState::Open => {} - WriteState::Closing => { - substream_state.close_waker = Some(cx.waker().clone()); - return Poll::Pending; - } - WriteState::Closed => return Poll::Ready(Ok(())), - WriteState::Stopped => { - let err = quinn_proto::FinishError::Stopped(0u32.into()); - return Poll::Ready(Err(io::Error::new(io::ErrorKind::ConnectionReset, err))); - } - } - - match inner.connection.send_stream(self.id).finish() { - Ok(()) => { - let substream_state = inner.unchecked_substream_state(self.id); - substream_state.close_waker = Some(cx.waker().clone()); - substream_state.write_state = WriteState::Closing; - Poll::Pending - } - Err(err @ quinn_proto::FinishError::Stopped(_)) => { - Poll::Ready(Err(io::Error::new(io::ErrorKind::ConnectionReset, err))) - } - Err(quinn_proto::FinishError::UnknownStream) => { - // We never make up IDs so the stream must have existed at some point if we get to here. - // `UnknownStream` is also emitted in case the stream is already finished, hence just - // return `Ok(())` here. - Poll::Ready(Ok(())) - } - } - } -} - -impl Drop for Substream { - fn drop(&mut self) { - let mut state = self.state.lock(); - state.substreams.remove(&self.id); - // Send `STOP_STREAM` if the remote did not finish the stream yet. - // We have to manually check the read stream since we might have - // received a `FIN` (without any other stream data) after the last - // time we tried to read. - let mut is_read_done = false; - if let Ok(mut chunks) = state.connection.recv_stream(self.id).read(true) { - if let Ok(chunk) = chunks.next(0) { - is_read_done = chunk.is_none(); - } - let _ = chunks.finalize(); - } - if !is_read_done { - let _ = state.connection.recv_stream(self.id).stop(0u32.into()); - } - // Close the writing side. - let mut send_stream = state.connection.send_stream(self.id); - match send_stream.finish() { - Ok(()) => {} - // Already finished or reset, which is fine. - Err(quinn_proto::FinishError::UnknownStream) => {} - Err(quinn_proto::FinishError::Stopped(reason)) => { - let _ = send_stream.reset(reason); - } - } - } -} - -#[derive(Debug, Default, Clone)] -pub enum WriteState { - /// The stream is open for writing. - #[default] - Open, - /// The writing side of the stream is closing. - Closing, - /// All data was successfully sent to the remote and the stream closed, - /// i.e. a [`quinn_proto::StreamEvent::Finished`] was reported for it. - Closed, - /// The stream was stopped by the remote before all data could be - /// sent. - Stopped, -} - -*/ \ No newline at end of file diff --git a/transports/quic/src/endpoint.rs b/transports/quic/src/endpoint.rs index 41f891684d4..14608a184d7 100644 --- a/transports/quic/src/endpoint.rs +++ b/transports/quic/src/endpoint.rs @@ -151,506 +151,6 @@ impl From for QuinnConfig { } } -/* -/// Channel used to send commands to the [`Driver`]. -#[derive(Debug, Clone)] -pub struct Channel { - /// Channel to the background of the endpoint. - to_endpoint: mpsc::Sender, - /// Address that the socket is bound to. - /// Note: this may be a wildcard ip address. - socket_addr: SocketAddr, -} - -impl Channel { - /// Builds a new endpoint that is listening on the [`SocketAddr`]. - pub fn new_bidirectional( - quinn_config: QuinnConfig, - socket_addr: SocketAddr, - ) -> Result<(Self, mpsc::Receiver), Error> { - // Channel for forwarding new inbound connections to the listener. - let (new_connections_tx, new_connections_rx) = mpsc::channel(CHANNEL_CAPACITY); - let endpoint = Self::new::

(quinn_config, socket_addr, Some(new_connections_tx))?; - Ok((endpoint, new_connections_rx)) - } - - /// Builds a new endpoint that only supports outbound connections. - pub fn new_dialer( - quinn_config: QuinnConfig, - socket_family: SocketFamily, - ) -> Result { - let socket_addr = match socket_family { - SocketFamily::Ipv4 => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), - SocketFamily::Ipv6 => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), - }; - Self::new::

(quinn_config, socket_addr, None) - } - - /// Spawn a new [`Driver`] that runs in the background. - fn new( - quinn_config: QuinnConfig, - socket_addr: SocketAddr, - new_connections: Option>, - ) -> Result { - let socket = std::net::UdpSocket::bind(socket_addr)?; - // NOT blocking, as per man:bind(2), as we pass an IP address. - socket.set_nonblocking(true)?; - // Capacity 0 to back-pressure the rest of the application if - // the udp socket is busy. - let (to_endpoint_tx, to_endpoint_rx) = mpsc::channel(0); - - let channel = Self { - to_endpoint: to_endpoint_tx, - socket_addr: socket.local_addr()?, - }; - - let server_config = new_connections - .is_some() - .then_some(quinn_config.server_config); - - let provider_socket = P::from_socket(socket)?; - - let driver = Driver::

::new( - quinn_config.endpoint_config, - quinn_config.client_config, - new_connections, - server_config, - channel.clone(), - provider_socket, - to_endpoint_rx, - ); - - // Drive the endpoint future in the background. - P::spawn(driver); - - Ok(channel) - } - - pub fn socket_addr(&self) -> &SocketAddr { - &self.socket_addr - } - - /// Try to send a message to the background task without blocking. - /// - /// This first polls the channel for capacity. - /// If the channel is full, the message is returned in `Ok(Err(_))` - /// and the context's waker is registered for wake-up. - /// - /// If the background task crashed `Err` is returned. - pub fn try_send( - &mut self, - to_endpoint: ToEndpoint, - cx: &mut Context<'_>, - ) -> Result, Disconnected> { - match self.to_endpoint.poll_ready_unpin(cx) { - Poll::Ready(Ok(())) => {} - Poll::Ready(Err(e)) => { - debug_assert!( - e.is_disconnected(), - "mpsc::Sender can only be disconnected when calling `poll_ready_unpin" - ); - - return Err(Disconnected {}); - } - Poll::Pending => return Ok(Err(to_endpoint)), - }; - - if let Err(e) = self.to_endpoint.start_send(to_endpoint) { - debug_assert!(e.is_disconnected(), "We called `Sink::poll_ready` so we are guaranteed to have a slot. If this fails, it means we are disconnected."); - - return Err(Disconnected {}); - } - - Ok(Ok(())) - } - - /// Send a message to inform the [`Driver`] about an - /// event caused by the owner of this [`Channel`] dropping. - /// This clones the sender to the endpoint to guarantee delivery. - /// This should *not* be called for regular messages. - pub fn send_on_drop(&mut self, to_endpoint: ToEndpoint) { - let _ = self.to_endpoint.clone().try_send(to_endpoint); - } -} -*/ - #[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)] #[error("Background task disconnected")] pub struct Disconnected {} - -/* -/// Message sent to the endpoint background task. -#[derive(Debug)] -pub enum ToEndpoint { - /// Instruct the [`quinn_proto::Endpoint`] to start connecting to the given address. - Dial { - /// UDP address to connect to. - addr: SocketAddr, - /// Version to dial the remote on. - version: ProtocolVersion, - /// Channel to return the result of the dialing to. - result: oneshot::Sender>, - }, - /// Send by a [`quinn_proto::Connection`] when the endpoint needs to process an event generated - /// by a connection. The event itself is opaque to us. Only `quinn_proto` knows what is in - /// there. - ProcessConnectionEvent { - connection_id: quinn_proto::ConnectionHandle, - event: quinn_proto::EndpointEvent, - }, - /// Instruct the endpoint to send a packet of data on its UDP socket. - SendUdpPacket(quinn_proto::Transmit), - /// The [`GenTransport`][crate::GenTransport] dialer or listener coupled to this endpoint - /// was dropped. - /// Once all pending connections are closed, the [`Driver`] should shut down. - Decoupled, -} - -/// Driver that runs in the background for as long as the endpoint is alive. Responsible for -/// processing messages and the UDP socket. -/// -/// # Behaviour -/// -/// This background task is responsible for the following: -/// -/// - Sending packets on the UDP socket. -/// - Receiving packets from the UDP socket and feed them to the [`quinn_proto::Endpoint`] state -/// machine. -/// - Transmitting events generated by the [`quinn_proto::Endpoint`] to the corresponding -/// [`crate::Connection`]. -/// - Receiving messages from the `rx` and processing the requested actions. This includes -/// UDP packets to send and events emitted by the [`crate::Connection`] objects. -/// - Sending new connections on `new_connection_tx`. -/// -/// When it comes to channels, there exists three main multi-producer-single-consumer channels -/// in play: -/// -/// - One channel, represented by `EndpointChannel::to_endpoint` and `Driver::rx`, -/// that communicates messages from [`Channel`] to the [`Driver`]. -/// - One channel for each existing connection that communicates messages from the -/// [`Driver` to that [`crate::Connection`]. -/// - One channel for the [`Driver`] to send newly-opened connections to. The receiving -/// side is processed by the [`GenTransport`][crate::GenTransport]. -/// -/// -/// ## Back-pressure -/// -/// ### If writing to the UDP socket is blocked -/// -/// In order to avoid an unbounded buffering of events, we prioritize sending data on the UDP -/// socket over everything else. Messages from the rest of the application sent through the -/// [`Channel`] are only processed if the UDP socket is ready so that we propagate back-pressure -/// in case of a busy socket. For connections, thus this eventually also back-pressures the -/// `AsyncWrite`on substreams. -/// -/// -/// ### Back-pressuring the remote if the application is busy -/// -/// If the channel to a connection is full because the connection is busy, inbound datagrams -/// for that connection are dropped so that the remote is backpressured. -/// The same applies for new connections if the transport is too busy to received it. -/// -/// -/// # Shutdown -/// -/// The background task shuts down if an [`ToEndpoint::Decoupled`] event was received and the -/// last active connection has drained. -#[derive(Debug)] -pub struct Driver { - // The actual QUIC state machine. - endpoint: quinn_proto::Endpoint, - // QuinnConfig for client connections. - client_config: quinn_proto::ClientConfig, - // Copy of the channel to the endpoint driver that is passed to each new connection. - channel: Channel, - // Channel to receive messages from the transport or connections. - rx: mpsc::Receiver, - - // Socket for sending and receiving datagrams. - provider_socket: P, - // Future for writing the next packet to the socket. - next_packet_out: Option, - - // List of all active connections, with a sender to notify them of events. - alive_connections: - HashMap>, - // Channel to forward new inbound connections to the transport. - // `None` if server capabilities are disabled, i.e. the endpoint is only used for dialing. - new_connection_tx: Option>, - // Whether the transport dropped its handle for this endpoint. - is_decoupled: bool, -} - -impl Driver

{ - fn new( - endpoint_config: Arc, - client_config: quinn_proto::ClientConfig, - new_connection_tx: Option>, - server_config: Option>, - channel: Channel, - socket: P, - rx: mpsc::Receiver, - ) -> Self { - Driver { - endpoint: quinn_proto::Endpoint::new(endpoint_config, server_config), - client_config, - channel, - rx, - provider_socket: socket, - next_packet_out: None, - alive_connections: HashMap::new(), - new_connection_tx, - is_decoupled: false, - } - } - - /// Handle a message sent from either the [`GenTransport`](super::GenTransport) - /// or a [`crate::Connection`]. - fn handle_message( - &mut self, - to_endpoint: ToEndpoint, - ) -> ControlFlow<(), Option> { - match to_endpoint { - ToEndpoint::Dial { - addr, - result, - version, - } => { - let mut config = self.client_config.clone(); - if version == ProtocolVersion::Draft29 { - config.version(0xff00_001d); - } - // This `"l"` seems necessary because an empty string is an invalid domain - // name. While we don't use domain names, the underlying rustls library - // is based upon the assumption that we do. - let (connection_id, connection) = match self.endpoint.connect(config, addr, "l") { - Ok(c) => c, - Err(err) => { - let _ = result.send(Err(ConnectError::from(err).into())); - return ControlFlow::Continue(None); - } - }; - - debug_assert_eq!(connection.side(), quinn_proto::Side::Client); - let (tx, rx) = mpsc::channel(CHANNEL_CAPACITY); - let connection = Connection::from_quinn_connection( - self.channel.clone(), - connection, - connection_id, - rx, - ); - self.alive_connections.insert(connection_id, tx); - let _ = result.send(Ok(connection)); - } - - // A connection wants to notify the endpoint of something. - ToEndpoint::ProcessConnectionEvent { - connection_id, - event, - } => { - let has_key = self.alive_connections.contains_key(&connection_id); - if !has_key { - return ControlFlow::Continue(None); - } - // We "drained" event indicates that the connection no longer exists and - // its ID can be reclaimed. - let is_drained_event = event.is_drained(); - if is_drained_event { - self.alive_connections.remove(&connection_id); - if self.is_decoupled && self.alive_connections.is_empty() { - log::info!( - "Driver is decoupled and no active connections remain. Shutting down." - ); - return ControlFlow::Break(()); - } - } - - let event_back = self.endpoint.handle_event(connection_id, event); - - if let Some(event_back) = event_back { - debug_assert!(!is_drained_event); - if let Some(sender) = self.alive_connections.get_mut(&connection_id) { - // We clone the sender to guarantee that there will be at least one - // free slot to send the event. - // The channel can not grow out of bound because an `event_back` is - // only sent if we previously received an event from the same connection. - // If the connection is busy, it won't sent us any more events to handle. - let _ = sender.clone().start_send(event_back); - } else { - log::error!("State mismatch: event for closed connection"); - } - } - } - - // Data needs to be sent on the UDP socket. - ToEndpoint::SendUdpPacket(transmit) => return ControlFlow::Continue(Some(transmit)), - ToEndpoint::Decoupled => self.handle_decoupling()?, - } - ControlFlow::Continue(None) - } - - /// Handle an UDP datagram received on the socket. - /// The datagram content was written into the `socket_recv_buffer`. - fn handle_datagram(&mut self, packet: BytesMut, packet_src: SocketAddr) -> ControlFlow<()> { - let local_ip = self.channel.socket_addr.ip(); - // TODO: ECN bits aren't handled - let (connec_id, event) = - match self - .endpoint - .handle(Instant::now(), packet_src, Some(local_ip), None, packet) - { - Some(event) => event, - None => return ControlFlow::Continue(()), - }; - match event { - quinn_proto::DatagramEvent::ConnectionEvent(event) => { - // `event` has type `quinn_proto::ConnectionEvent`, which has multiple - // variants. `quinn_proto::Endpoint::handle` however only ever returns - // `ConnectionEvent::Datagram`. - debug_assert!(format!("{event:?}").contains("Datagram")); - - // Redirect the datagram to its connection. - if let Some(sender) = self.alive_connections.get_mut(&connec_id) { - match sender.try_send(event) { - Ok(()) => {} - Err(err) if err.is_disconnected() => { - // Connection was dropped by the user. - // Inform the endpoint that this connection is drained. - self.endpoint - .handle_event(connec_id, quinn_proto::EndpointEvent::drained()); - self.alive_connections.remove(&connec_id); - } - Err(err) if err.is_full() => { - // Connection is too busy. Drop the datagram to back-pressure the remote. - log::debug!( - "Dropping packet for connection {:?} because the connection's channel is full.", - connec_id - ); - } - Err(_) => unreachable!("Error is either `Full` or `Disconnected`."), - } - } else { - log::error!("State mismatch: event for closed connection"); - } - } - quinn_proto::DatagramEvent::NewConnection(connec) => { - // A new connection has been received. `connec_id` is a newly-allocated - // identifier. - debug_assert_eq!(connec.side(), quinn_proto::Side::Server); - let connection_tx = match self.new_connection_tx.as_mut() { - Some(tx) => tx, - None => { - debug_assert!(false, "Endpoint reported a new connection even though server capabilities are disabled."); - return ControlFlow::Continue(()); - } - }; - - let (tx, rx) = mpsc::channel(CHANNEL_CAPACITY); - let connection = - Connection::from_quinn_connection(self.channel.clone(), connec, connec_id, rx); - match connection_tx.start_send(connection) { - Ok(()) => { - self.alive_connections.insert(connec_id, tx); - } - Err(e) if e.is_disconnected() => self.handle_decoupling()?, - Err(e) if e.is_full() => log::warn!( - "Dropping new incoming connection {:?} because the channel to the listener is full", - connec_id - ), - Err(_) => unreachable!("Error is either `Full` or `Disconnected`."), - } - } - } - ControlFlow::Continue(()) - } - - /// The transport dropped the channel to this [`Driver`]. - fn handle_decoupling(&mut self) -> ControlFlow<()> { - if self.alive_connections.is_empty() { - return ControlFlow::Break(()); - } - // Listener was closed. - self.endpoint.reject_new_connections(); - self.new_connection_tx = None; - self.is_decoupled = true; - ControlFlow::Continue(()) - } -} - -/// Future that runs until the [`Driver`] is decoupled and not active connections -/// remain -impl Future for Driver

{ - type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - loop { - // Flush any pending pocket so that the socket is reading to write an next - // packet. - match self.provider_socket.poll_send_flush(cx) { - // The pending packet was send or no packet was pending. - Poll::Ready(Ok(_)) => { - // Start sending a packet on the socket. - if let Some(transmit) = self.next_packet_out.take() { - self.provider_socket - .start_send(transmit.contents, transmit.destination); - continue; - } - - // The endpoint might request packets to be sent out. This is handled in - // priority to avoid buffering up packets. - if let Some(transmit) = self.endpoint.poll_transmit() { - self.next_packet_out = Some(transmit); - continue; - } - - // Handle messages from transport and connections. - match self.rx.poll_next_unpin(cx) { - Poll::Ready(Some(to_endpoint)) => match self.handle_message(to_endpoint) { - ControlFlow::Continue(Some(transmit)) => { - self.next_packet_out = Some(transmit); - continue; - } - ControlFlow::Continue(None) => continue, - ControlFlow::Break(()) => break, - }, - Poll::Ready(None) => { - unreachable!("Sender side is never dropped or closed.") - } - Poll::Pending => {} - } - } - // Errors on the socket are expected to never happen, and we handle them by simply - // printing a log message. The packet gets discarded in case of error, but we are - // robust to packet losses and it is consequently not a logic error to proceed with - // normal operations. - Poll::Ready(Err(err)) => { - log::warn!("Error while sending on QUIC UDP socket: {:?}", err); - continue; - } - Poll::Pending => {} - } - - // Poll for new packets from the remote. - match self.provider_socket.poll_recv_from(cx) { - Poll::Ready(Ok((bytes, packet_src))) => { - let bytes_mut = bytes.as_slice().into(); - match self.handle_datagram(bytes_mut, packet_src) { - ControlFlow::Continue(()) => continue, - ControlFlow::Break(()) => break, - } - } - // Errors on the socket are expected to never happen, and we handle them by - // simply printing a log message. - Poll::Ready(Err(err)) => { - log::warn!("Error while receive on QUIC UDP socket: {:?}", err); - continue; - } - Poll::Pending => {} - } - - return Poll::Pending; - } - - Poll::Ready(()) - } -} -*/ diff --git a/transports/quic/src/provider.rs b/transports/quic/src/provider.rs index 3f4acf572f1..953c1cfd275 100644 --- a/transports/quic/src/provider.rs +++ b/transports/quic/src/provider.rs @@ -40,27 +40,6 @@ pub trait Provider: Unpin + Send + Sized + 'static { type IfWatcher: Unpin + Send; type Runtime: quinn::Runtime; - // /// Create a new providing that is wrapping the socket. - // /// - // /// Note: The socket must be set to non-blocking. - // fn from_socket(socket: std::net::UdpSocket) -> io::Result; - - // /// Receive a single packet. - // /// - // /// Returns the message and the address the message came from. - // fn poll_recv_from(&mut self, cx: &mut Context<'_>) -> Poll, SocketAddr)>>; - - // /// Set sending a packet on the socket. - // /// - // /// Since only one packet can be sent at a time, this may only be called if a preceding - // /// call to [`Provider::poll_send_flush`] returned [`Poll::Ready`]. - // fn start_send(&mut self, data: Vec, addr: SocketAddr); - - // /// Flush a packet send in [`Provider::start_send`]. - // /// - // /// If [`Poll::Ready`] is returned the socket is ready for sending a new packet. - // fn poll_send_flush(&mut self, cx: &mut Context<'_>) -> Poll>; - /// Run the corresponding runtime fn runtime() -> Self::Runtime; diff --git a/transports/quic/src/provider/async_std.rs b/transports/quic/src/provider/async_std.rs index b748d3af92a..1ec4aad1f4c 100644 --- a/transports/quic/src/provider/async_std.rs +++ b/transports/quic/src/provider/async_std.rs @@ -39,51 +39,6 @@ impl super::Provider for Provider { type IfWatcher = if_watch::smol::IfWatcher; type Runtime = quinn::AsyncStdRuntime; - /* - fn from_socket(socket: std::net::UdpSocket) -> io::Result { - let socket = Arc::new(socket.into()); - let recv_stream = ReceiveStream::new(Arc::clone(&socket)); - Ok(Provider { - socket, - send_packet: None, - recv_stream, - }) - } - - fn poll_recv_from(&mut self, cx: &mut Context<'_>) -> Poll, SocketAddr)>> { - match self.recv_stream.poll_next_unpin(cx) { - Poll::Ready(ready) => { - Poll::Ready(ready.expect("ReceiveStream::poll_next never returns None.")) - } - Poll::Pending => Poll::Pending, - } - } - - fn start_send(&mut self, data: Vec, addr: SocketAddr) { - let socket = self.socket.clone(); - let send = async move { - socket.send_to(&data, addr).await?; - Ok(()) - } - .boxed(); - self.send_packet = Some(send) - } - - fn poll_send_flush(&mut self, cx: &mut Context<'_>) -> Poll> { - let pending = match self.send_packet.as_mut() { - Some(pending) => pending, - None => return Poll::Ready(Ok(())), - }; - match pending.poll_unpin(cx) { - Poll::Ready(result) => { - self.send_packet = None; - Poll::Ready(result) - } - Poll::Pending => Poll::Pending, - } - } - */ - fn runtime() -> Self::Runtime { quinn::AsyncStdRuntime } @@ -103,49 +58,3 @@ impl super::Provider for Provider { watcher.poll_if_event(cx) } } - -/* -type ReceiveStreamItem = ( - Result<(usize, SocketAddr), io::Error>, - Arc, - Vec, -); - -/// Wrapper around the socket to implement `Stream` on it. -struct ReceiveStream { - /// Future for receiving a packet on the socket. - // This is needed since [`async_Std::net::UdpSocket`] does not - // provide a poll-style interface for receiving packets. - fut: BoxFuture<'static, ReceiveStreamItem>, -} - -impl ReceiveStream { - fn new(socket: Arc) -> Self { - let fut = ReceiveStream::next(socket, vec![0; super::RECEIVE_BUFFER_SIZE]).boxed(); - Self { fut: fut.boxed() } - } - - async fn next(socket: Arc, mut socket_recv_buffer: Vec) -> ReceiveStreamItem { - let recv = socket.recv_from(&mut socket_recv_buffer).await; - (recv, socket, socket_recv_buffer) - } -} - -impl Stream for ReceiveStream { - type Item = Result<(Vec, SocketAddr), io::Error>; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let (result, socket, buffer) = ready!(self.fut.poll_unpin(cx)); - - let result = result.map(|(packet_len, packet_src)| { - debug_assert!(packet_len <= buffer.len()); - // Copies the bytes from the `socket_recv_buffer` they were written into. - (buffer[..packet_len].into(), packet_src) - }); - // Set the future for receiving the next packet on the stream. - self.fut = ReceiveStream::next(socket, buffer).boxed(); - - Poll::Ready(Some(result)) - } -} -*/ diff --git a/transports/quic/src/provider/tokio.rs b/transports/quic/src/provider/tokio.rs index 1d0416e78cf..4be97771ce6 100644 --- a/transports/quic/src/provider/tokio.rs +++ b/transports/quic/src/provider/tokio.rs @@ -38,47 +38,6 @@ impl super::Provider for Provider { type IfWatcher = if_watch::tokio::IfWatcher; type Runtime = quinn::TokioRuntime; - /* - fn from_socket(socket: std::net::UdpSocket) -> std::io::Result { - let socket = UdpSocket::from_std(socket)?; - Ok(Provider { - socket, - socket_recv_buffer: vec![0; super::RECEIVE_BUFFER_SIZE], - next_packet_out: None, - }) - } - - fn poll_send_flush(&mut self, cx: &mut Context<'_>) -> Poll> { - let (data, addr) = match self.next_packet_out.as_ref() { - Some(pending) => pending, - None => return Poll::Ready(Ok(())), - }; - match self.socket.poll_send_to(cx, data.as_slice(), *addr) { - Poll::Ready(result) => { - self.next_packet_out = None; - Poll::Ready(result.map(|_| ())) - } - Poll::Pending => Poll::Pending, - } - } - - fn poll_recv_from(&mut self, cx: &mut Context<'_>) -> Poll, SocketAddr)>> { - let Self { - socket, - socket_recv_buffer, - .. - } = self; - let mut read_buf = ReadBuf::new(socket_recv_buffer.as_mut_slice()); - let packet_src = ready!(socket.poll_recv_from(cx, &mut read_buf)?); - let bytes = read_buf.filled().to_vec(); - Poll::Ready(Ok((bytes, packet_src))) - } - - fn start_send(&mut self, data: Vec, addr: SocketAddr) { - self.next_packet_out = Some((data, addr)); - } - */ - fn runtime() -> Self::Runtime { quinn::TokioRuntime } diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index f5f7a5498bc..9c075dabb5a 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -261,100 +261,6 @@ impl From for TransportError { } } -/* -/// Dialer for addresses if no matching listener exists. -#[derive(Debug)] -struct Dialer { - /// Channel to the [`crate::endpoint::Driver`] that - /// is driving the endpoint. - endpoint_channel: endpoint::Channel, - /// Queued dials for the endpoint. - state: DialerState, -} - -impl Dialer { - fn new( - config: QuinnConfig, - socket_family: SocketFamily, - ) -> Result> { - let endpoint_channel = endpoint::Channel::new_dialer::

(config, socket_family) - .map_err(TransportError::Other)?; - Ok(Dialer { - endpoint_channel, - state: DialerState::default(), - }) - } - - fn poll(&mut self, cx: &mut Context<'_>) -> Poll { - self.state.poll(&mut self.endpoint_channel, cx) - } -} - -impl Drop for Dialer { - fn drop(&mut self) { - self.endpoint_channel.send_on_drop(ToEndpoint::Decoupled); - } -} - -/// Pending dials to be sent to the endpoint was the [`endpoint::Channel`] -/// has capacity -#[derive(Default, Debug)] -struct DialerState { - pending_dials: VecDeque, - waker: Option, -} - -impl DialerState { - fn new_dial( - &mut self, - address: SocketAddr, - timeout: Duration, - version: ProtocolVersion, - ) -> BoxFuture<'static, Result<(PeerId, Connection), Error>> { - let (rx, tx) = oneshot::channel(); - - let message = ToEndpoint::Dial { - addr: address, - result: rx, - version, - }; - - self.pending_dials.push_back(message); - - if let Some(waker) = self.waker.take() { - waker.wake(); - } - - async move { - // Our oneshot getting dropped means the message didn't make it to the endpoint driver. - let connection = tx.await.map_err(|_| Error::EndpointDriverCrashed)??; - let (peer, connection) = Connecting::new(connection, timeout).await?; - - Ok((peer, connection)) - } - .boxed() - } - - /// Send all pending dials into the given [`endpoint::Channel`]. - /// - /// This only ever returns [`Poll::Pending`], or an error in case the channel is closed. - fn poll(&mut self, channel: &mut endpoint::Channel, cx: &mut Context<'_>) -> Poll { - while let Some(to_endpoint) = self.pending_dials.pop_front() { - match channel.try_send(to_endpoint, cx) { - Ok(Ok(())) => {} - Ok(Err(to_endpoint)) => { - self.pending_dials.push_front(to_endpoint); - break; - } - Err(endpoint::Disconnected {}) => return Poll::Ready(Error::EndpointDriverCrashed), - } - } - self.waker = Some(cx.waker().clone()); - Poll::Pending - } -} -*/ - /// Listener for incoming connections. struct Listener { /// Id of the listener. From ce2403d15cb86d7f53560106f7319ea82f4a79d3 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Mon, 13 Feb 2023 16:27:09 +0000 Subject: [PATCH 10/60] Remove quinn-proto dep --- Cargo.lock | 1 - transports/quic/Cargo.toml | 1 - transports/quic/src/endpoint.rs | 16 ++++++++-------- transports/quic/src/lib.rs | 4 ++-- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 882e0432a76..77a5838a1a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2614,7 +2614,6 @@ dependencies = [ "parking_lot 0.12.1", "quickcheck", "quinn", - "quinn-proto", "rand 0.8.5", "rustls 0.20.8", "thiserror", diff --git a/transports/quic/Cargo.toml b/transports/quic/Cargo.toml index a9ddc5324c2..13df55b24c1 100644 --- a/transports/quic/Cargo.toml +++ b/transports/quic/Cargo.toml @@ -18,7 +18,6 @@ libp2p-core = { version = "0.39.0", path = "../../core" } libp2p-tls = { version = "0.1.0-alpha.1", path = "../tls" } log = "0.4" parking_lot = "0.12.0" -quinn-proto = { version = "0.9.0", default-features = false, features = ["tls-rustls"] } quinn = { version = "0.9.0", default-features = false, features = ["tls-rustls", "futures-io"] } rand = "0.8.5" rustls = { version = "0.20.2", default-features = false } diff --git a/transports/quic/src/endpoint.rs b/transports/quic/src/endpoint.rs index 14608a184d7..6184fd4bb06 100644 --- a/transports/quic/src/endpoint.rs +++ b/transports/quic/src/endpoint.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use quinn_proto::VarInt; +use quinn::VarInt; use std::{ sync::Arc, time::Duration, @@ -97,9 +97,9 @@ impl Config { /// Represents the inner configuration for [`quinn_proto`]. #[derive(Debug, Clone)] pub struct QuinnConfig { - pub(crate) client_config: quinn_proto::ClientConfig, - pub(crate) server_config: Arc, - pub(crate) endpoint_config: Arc, + pub(crate) client_config: quinn::ClientConfig, + pub(crate) server_config: Arc, + pub(crate) endpoint_config: Arc, } impl From for QuinnConfig { @@ -115,7 +115,7 @@ impl From for QuinnConfig { support_draft_29, handshake_timeout: _, } = config; - let mut transport = quinn_proto::TransportConfig::default(); + let mut transport = quinn::TransportConfig::default(); // Disable uni-directional streams. transport.max_concurrent_uni_streams(0u32.into()); transport.max_concurrent_bidi_streams(max_concurrent_stream_limit.into()); @@ -128,17 +128,17 @@ impl From for QuinnConfig { transport.receive_window(max_connection_data.into()); let transport = Arc::new(transport); - let mut server_config = quinn_proto::ServerConfig::with_crypto(server_tls_config); + let mut server_config = quinn::ServerConfig::with_crypto(server_tls_config); server_config.transport = Arc::clone(&transport); // Disables connection migration. // Long-term this should be enabled, however we then need to handle address change // on connections in the `Connection`. server_config.migration(false); - let mut client_config = quinn_proto::ClientConfig::new(client_tls_config); + let mut client_config = quinn::ClientConfig::new(client_tls_config); client_config.transport_config(transport); - let mut endpoint_config = quinn_proto::EndpointConfig::default(); + let mut endpoint_config = quinn::EndpointConfig::default(); if !support_draft_29 { endpoint_config.supported_versions(vec![1]); } diff --git a/transports/quic/src/lib.rs b/transports/quic/src/lib.rs index 8195bb3b136..c3660fe7b46 100644 --- a/transports/quic/src/lib.rs +++ b/transports/quic/src/lib.rs @@ -99,9 +99,9 @@ pub enum Error { /// Dialing a remote peer failed. #[derive(Debug, thiserror::Error)] #[error(transparent)] -pub struct ConnectError(#[from] quinn_proto::ConnectError); +pub struct ConnectError(#[from] quinn::ConnectError); /// Error on an established [`Connection`]. #[derive(Debug, thiserror::Error)] #[error(transparent)] -pub struct ConnectionError(#[from] quinn_proto::ConnectionError); +pub struct ConnectionError(#[from] quinn::ConnectionError); From b1560dd9037da894d16147db64c27baea725521a Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 14 Feb 2023 00:40:26 +0000 Subject: [PATCH 11/60] Remove redundant Arcs --- transports/quic/src/endpoint.rs | 8 ++++---- transports/quic/src/transport.rs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/transports/quic/src/endpoint.rs b/transports/quic/src/endpoint.rs index 6184fd4bb06..87beedaf177 100644 --- a/transports/quic/src/endpoint.rs +++ b/transports/quic/src/endpoint.rs @@ -98,8 +98,8 @@ impl Config { #[derive(Debug, Clone)] pub struct QuinnConfig { pub(crate) client_config: quinn::ClientConfig, - pub(crate) server_config: Arc, - pub(crate) endpoint_config: Arc, + pub(crate) server_config: quinn::ServerConfig, + pub(crate) endpoint_config: quinn::EndpointConfig, } impl From for QuinnConfig { @@ -145,8 +145,8 @@ impl From for QuinnConfig { QuinnConfig { client_config, - server_config: Arc::new(server_config), - endpoint_config: Arc::new(endpoint_config), + server_config, + endpoint_config, } } } diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index 9c075dabb5a..578abf914a9 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -104,8 +104,8 @@ impl Transport for GenTransport

{ fn listen_on(&mut self, addr: Multiaddr) -> Result> { let (socket_addr, version) = multiaddr_to_socketaddr(&addr, self.support_draft_29) .ok_or(TransportError::MultiaddrNotSupported(addr))?; - let endpoint_config = quinn::EndpointConfig::clone(&self.quinn_config.endpoint_config); - let server_config = quinn::ServerConfig::clone(&self.quinn_config.server_config); + let endpoint_config = self.quinn_config.endpoint_config.clone(); + let server_config = self.quinn_config.server_config.clone(); let endpoint = Self::new_endpoint(endpoint_config, Some(server_config), socket_addr)?; let listener_id = ListenerId::new(); let listener = Listener::new( @@ -183,7 +183,7 @@ impl Transport for GenTransport

{ SocketFamily::Ipv4 => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), SocketFamily::Ipv6 => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), }; - let endpoint_config = quinn::EndpointConfig::clone(&self.quinn_config.endpoint_config); + let endpoint_config = self.quinn_config.endpoint_config.clone(); let endpoint = Self::new_endpoint(endpoint_config, None, listen_socket_addr)?; vacant.insert(endpoint.clone()); @@ -202,7 +202,7 @@ impl Transport for GenTransport

{ } }; let handshake_timeout = self.handshake_timeout; - let mut client_config = quinn::ClientConfig::clone(&self.quinn_config.client_config); + let mut client_config = self.quinn_config.client_config.clone(); if version == ProtocolVersion::Draft29 { client_config.version(0xff00_001d); } From 8171862b2b0b2a2817b28592b5cd9c10660b77de Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 14 Feb 2023 00:44:30 +0000 Subject: [PATCH 12/60] Add more comments --- transports/quic/src/connection.rs | 22 ++++++++++++++++++++ transports/quic/src/connection/connecting.rs | 10 +-------- transports/quic/src/connection/substream.rs | 1 + transports/quic/src/provider.rs | 3 +-- transports/quic/src/provider/async_std.rs | 4 +--- transports/quic/src/provider/tokio.rs | 4 +--- transports/quic/src/transport.rs | 1 + 7 files changed, 28 insertions(+), 17 deletions(-) diff --git a/transports/quic/src/connection.rs b/transports/quic/src/connection.rs index 88ed12d16ec..10eeff984d3 100644 --- a/transports/quic/src/connection.rs +++ b/transports/quic/src/connection.rs @@ -34,12 +34,32 @@ use std::{ /// State for a single opened QUIC connection. pub struct Connection { connection: quinn::Connection, + /// Future for accepting a new incoming bidirectional stream. incoming: BoxFuture<'static, Result<(quinn::SendStream, quinn::RecvStream), quinn::ConnectionError>>, + /// Future for opening a new outgoing a new bidirectional stream. outgoing: BoxFuture<'static, Result<(quinn::SendStream, quinn::RecvStream), quinn::ConnectionError>>, } +impl Connection { + /// Build a [`Connection`] from raw components. + /// + /// This function assumes that the [`quinn::Connection`] is completely fresh and none of + /// its methods has ever been called. Failure to comply might lead to logic errors and panics. + fn new(connection: quinn::Connection) -> Self { + let connection_c = connection.clone(); + let incoming = Box::pin(async move { connection_c.accept_bi().await }); + let connection_c = connection.clone(); + let outgoing = Box::pin(async move { connection_c.open_bi().await }); + Self { + connection, + incoming, + outgoing, + } + } +} + impl StreamMuxer for Connection { type Substream = Substream; type Error = quinn::ConnectionError; @@ -74,6 +94,8 @@ impl StreamMuxer for Connection { self: Pin<&mut Self>, _cx: &mut Context<'_>, ) -> Poll> { + // TODO: If connection migration is enabled (currently disabled) address + // change on the connection needs to be handled. Poll::Pending } diff --git a/transports/quic/src/connection/connecting.rs b/transports/quic/src/connection/connecting.rs index 136025920e1..0eea4b3353f 100644 --- a/transports/quic/src/connection/connecting.rs +++ b/transports/quic/src/connection/connecting.rs @@ -78,15 +78,7 @@ impl Future for Connecting { }; let peer_id = Self::remote_peer_id(&connection); - let connection_c = connection.clone(); - let incoming = Box::pin(async move { connection_c.accept_bi().await }); - let connection_c = connection.clone(); - let outgoing = Box::pin(async move { connection_c.open_bi().await }); - let muxer = Connection { - connection, - incoming, - outgoing, - }; + let muxer = Connection::new(connection); Poll::Ready(Ok((peer_id, muxer))) } } diff --git a/transports/quic/src/connection/substream.rs b/transports/quic/src/connection/substream.rs index 6dc101cdcf4..8a1f83d7571 100644 --- a/transports/quic/src/connection/substream.rs +++ b/transports/quic/src/connection/substream.rs @@ -26,6 +26,7 @@ use std::{ use futures::{AsyncRead, AsyncWrite}; +/// A single stream on a connection pub struct Substream { send: quinn::SendStream, recv: quinn::RecvStream, diff --git a/transports/quic/src/provider.rs b/transports/quic/src/provider.rs index 953c1cfd275..9eef57de9d2 100644 --- a/transports/quic/src/provider.rs +++ b/transports/quic/src/provider.rs @@ -34,8 +34,7 @@ pub mod tokio; #[cfg(any(feature = "async-std", feature = "tokio"))] const RECEIVE_BUFFER_SIZE: usize = 65536; -/// Provider for non-blocking receiving and sending on a [`std::net::UdpSocket`] -/// and spawning tasks. +/// Provider for a corresponding quinn runtime and spawning tasks. pub trait Provider: Unpin + Send + Sized + 'static { type IfWatcher: Unpin + Send; type Runtime: quinn::Runtime; diff --git a/transports/quic/src/provider/async_std.rs b/transports/quic/src/provider/async_std.rs index 1ec4aad1f4c..d5dc2cdf372 100644 --- a/transports/quic/src/provider/async_std.rs +++ b/transports/quic/src/provider/async_std.rs @@ -30,9 +30,7 @@ use crate::GenTransport; /// Transport with [`async-std`] runtime. pub type Transport = GenTransport; -// TODO docs -/// Provider for reading / writing to a sockets and spawning -/// tasks using [`async-std`]. +/// Provider for quinn runtime and spawning tasks using [`async-std`]. pub struct Provider; impl super::Provider for Provider { diff --git a/transports/quic/src/provider/tokio.rs b/transports/quic/src/provider/tokio.rs index 4be97771ce6..adeb0906183 100644 --- a/transports/quic/src/provider/tokio.rs +++ b/transports/quic/src/provider/tokio.rs @@ -29,9 +29,7 @@ use crate::GenTransport; /// Transport with [`tokio`] runtime. pub type Transport = GenTransport; -// TODO docs -/// Provider for reading / writing to a sockets and spawning -/// tasks using [`tokio`]. +/// Provider for quinn runtime and spawning tasks using [`tokio`]. pub struct Provider; impl super::Provider for Provider { diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index 578abf914a9..ea484f96650 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -88,6 +88,7 @@ impl GenTransport

{ support_draft_29, } } + /// Create a new [`quinn::Endpoint`] with the given configs. fn new_endpoint(endpoint_config: quinn::EndpointConfig, server_config: Option, socket_addr: SocketAddr) -> Result { let socket = UdpSocket::bind(socket_addr)?; let endpoint = quinn::Endpoint::new(endpoint_config, server_config, socket, P::runtime())?; From a1789af4872dcab5845f0ac099d578d81a38ccb2 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 14 Feb 2023 19:26:57 +0000 Subject: [PATCH 13/60] More comments --- transports/quic/src/connection/substream.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/transports/quic/src/connection/substream.rs b/transports/quic/src/connection/substream.rs index 8a1f83d7571..e1a0e0fd9f0 100644 --- a/transports/quic/src/connection/substream.rs +++ b/transports/quic/src/connection/substream.rs @@ -28,9 +28,11 @@ use futures::{AsyncRead, AsyncWrite}; /// A single stream on a connection pub struct Substream { + /// A send part of the stream send: quinn::SendStream, + /// A receive part of the stream recv: quinn::RecvStream, - closed: bool, + closed: bool, // TODO check whether this is necessary } impl Substream { From b46feb7a95369e86d4acd74b65fe59fe7e624512 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 14 Feb 2023 19:42:11 +0000 Subject: [PATCH 14/60] More comments --- transports/quic/src/connection.rs | 1 + transports/quic/src/transport.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/transports/quic/src/connection.rs b/transports/quic/src/connection.rs index 10eeff984d3..5dd2570f11a 100644 --- a/transports/quic/src/connection.rs +++ b/transports/quic/src/connection.rs @@ -33,6 +33,7 @@ use std::{ /// State for a single opened QUIC connection. pub struct Connection { + /// Underlying connection. connection: quinn::Connection, /// Future for accepting a new incoming bidirectional stream. incoming: diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index ea484f96650..3e6c77dce40 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -267,6 +267,7 @@ struct Listener { /// Id of the listener. listener_id: ListenerId, + /// Version of the supported quic protocol. version: ProtocolVersion, /// Endpoint From b947b128c4401960fc47cde270dfef3885def2bc Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 14 Feb 2023 19:43:18 +0000 Subject: [PATCH 15/60] Remove unused commented code --- transports/quic/src/connection/connecting.rs | 9 ++-- transports/quic/src/endpoint.rs | 11 ----- transports/quic/src/provider.rs | 4 -- transports/quic/src/transport.rs | 44 -------------------- 4 files changed, 3 insertions(+), 65 deletions(-) diff --git a/transports/quic/src/connection/connecting.rs b/transports/quic/src/connection/connecting.rs index 0eea4b3353f..ec581baaca4 100644 --- a/transports/quic/src/connection/connecting.rs +++ b/transports/quic/src/connection/connecting.rs @@ -22,7 +22,7 @@ use crate::{Connection, ConnectionError, Error}; -use futures::{prelude::*, future::Either}; +use futures::{prelude::*, future::{Either, select, Select}}; use futures_timer::Delay; use libp2p_core::PeerId; use std::{ @@ -34,15 +34,13 @@ use std::{ /// A QUIC connection currently being negotiated. #[derive(Debug)] pub struct Connecting { - connecting: futures::future::Select, - //timeout: Delay, + connecting: Select, } impl Connecting { pub(crate) fn new(connection: quinn::Connecting, timeout: Duration) -> Self { Connecting { - connecting: futures::future::select(connection, Delay::new(timeout)), - //timeout: Delay::new(timeout), + connecting: select(connection, Delay::new(timeout)), } } } @@ -51,7 +49,6 @@ impl Connecting { /// Returns the address of the node we're connected to. /// Panics if the connection is still handshaking. fn remote_peer_id(connection: &quinn::Connection) -> PeerId { - //debug_assert!(!connection.handshake_data().is_some()); let identity = connection .peer_identity() .expect("connection got identity because it passed TLS handshake; qed"); diff --git a/transports/quic/src/endpoint.rs b/transports/quic/src/endpoint.rs index 87beedaf177..6bc0ed8fcc0 100644 --- a/transports/quic/src/endpoint.rs +++ b/transports/quic/src/endpoint.rs @@ -24,13 +24,6 @@ use std::{ time::Duration, }; -// The `Driver` drops packets if the channel to the connection -// or transport is full. -// Set capacity 10 to avoid unnecessary packet drops if the receiver -// is only very briefly busy, but not buffer a large amount of packets -// if it is blocked longer. -const CHANNEL_CAPACITY: usize = 10; - /// Config for the transport. #[derive(Clone)] pub struct Config { @@ -150,7 +143,3 @@ impl From for QuinnConfig { } } } - -#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)] -#[error("Background task disconnected")] -pub struct Disconnected {} diff --git a/transports/quic/src/provider.rs b/transports/quic/src/provider.rs index 9eef57de9d2..8b616257b79 100644 --- a/transports/quic/src/provider.rs +++ b/transports/quic/src/provider.rs @@ -30,10 +30,6 @@ pub mod async_std; #[cfg(feature = "tokio")] pub mod tokio; -/// Size of the buffer for reading data 0x10000. -#[cfg(any(feature = "async-std", feature = "tokio"))] -const RECEIVE_BUFFER_SIZE: usize = 65536; - /// Provider for a corresponding quinn runtime and spawning tasks. pub trait Provider: Unpin + Send + Sized + 'static { type IfWatcher: Unpin + Send; diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index 3e6c77dce40..d47182fa6e5 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -232,21 +232,6 @@ impl Transport for GenTransport

{ mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - /* - let mut errored = Vec::new(); - for (key, dialer) in &mut self.dialer { - if let Poll::Ready(_error) = dialer.poll(cx) { - errored.push(*key); - } - } - - for key in errored { - // Endpoint driver of dialer crashed. - // Drop dialer and all pending dials so that the connection receiver is notified. - self.dialer.remove(&key); - } - */ - if let Poll::Ready(Some(ev)) = self.listeners.poll_next_unpin(cx) { return Poll::Ready(ev); } @@ -272,10 +257,6 @@ struct Listener { /// Endpoint endpoint: quinn::Endpoint, - // /// Channel to the endpoint to initiate dials. - // endpoint_channel: endpoint::Channel, - // /// Queued dials. - // dialer_state: DialerState, /// A future to poll new incoming connections. accept: BoxFuture<'static, Option>, @@ -302,13 +283,9 @@ impl Listener

{ listener_id: ListenerId, socket_addr: SocketAddr, endpoint: quinn::Endpoint, - // config: QuinnConfig, handshake_timeout: Duration, version: ProtocolVersion, ) -> Result { - // let (endpoint_channel, new_connections_rx) = - // endpoint::Channel::new_bidirectional::

(config, socket_addr)?; - let if_watcher; let pending_event; if socket_addr.ip().is_unspecified() { @@ -329,15 +306,12 @@ impl Listener

{ Ok(Listener { endpoint, accept, - // endpoint_channel, listener_id, version, - // new_connections_rx, handshake_timeout, if_watcher, is_closed: false, pending_event, - // dialer_state: DialerState::default(), close_listener_waker: None, }) } @@ -409,17 +383,6 @@ impl Listener

{ } } } - - // /// Poll [`DialerState`] to initiate requested dials. - // fn poll_dialer(&mut self, cx: &mut Context<'_>) -> Poll { - // let Self { - // dialer_state, - // endpoint_channel, - // .. - // } = self; - - // dialer_state.poll(endpoint_channel, cx) - // } } impl Stream for Listener

{ @@ -435,10 +398,6 @@ impl Stream for Listener

{ if let Poll::Ready(event) = self.poll_if_addr(cx) { return Poll::Ready(Some(event)); } - // if let Poll::Ready(error) = self.poll_dialer(cx) { - // self.close(Err(error)); - // continue; - // } match self.accept.poll_unpin(cx) { Poll::Ready(Some(connecting)) => { @@ -474,9 +433,6 @@ impl fmt::Debug for Listener

{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Listener") .field("listener_id", &self.listener_id) - //.field("endpoint_channel", &self.endpoint_channel) - //.field("dialer_state", &self.dialer_state) - //.field("new_connections_rx", &self.new_connections_rx) .field("handshake_timeout", &self.handshake_timeout) .field("is_closed", &self.is_closed) .field("pending_event", &self.pending_event) From 0bce4737a8fb5bed5704e6b76e69d7128c40d2ca Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 14 Feb 2023 19:52:47 +0000 Subject: [PATCH 16/60] Fix old tests to match new structs --- transports/quic/src/transport.rs | 194 ++++++++++++------------------- 1 file changed, 72 insertions(+), 122 deletions(-) diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index d47182fa6e5..da1b6b2280c 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -571,7 +571,6 @@ fn socketaddr_to_multiaddr(socket_addr: &SocketAddr, version: ProtocolVersion) - #[cfg(any(feature = "async-std", feature = "tokio"))] mod test { use futures::future::poll_fn; - use futures_timer::Delay; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use super::*; @@ -664,125 +663,76 @@ mod test { ); } - // #[cfg(feature = "async-std")] - // #[async_std::test] - // async fn test_close_listener() { - // let keypair = libp2p_core::identity::Keypair::generate_ed25519(); - // let config = Config::new(&keypair); - // let mut transport = crate::async_std::Transport::new(config); - // assert!(poll_fn(|cx| Pin::new(&mut transport).as_mut().poll(cx)) - // .now_or_never() - // .is_none()); - - // // Run test twice to check that there is no unexpected behaviour if `Transport.listener` - // // is temporarily empty. - // for _ in 0..2 { - // let id = transport - // .listen_on("/ip4/0.0.0.0/udp/0/quic-v1".parse().unwrap()) - // .unwrap(); - - // // Copy channel to use it later. - // let mut channel = transport - // .listeners - // .iter() - // .next() - // .unwrap() - // .endpoint_channel - // .clone(); - - // match poll_fn(|cx| Pin::new(&mut transport).as_mut().poll(cx)).await { - // TransportEvent::NewAddress { - // listener_id, - // listen_addr, - // } => { - // assert_eq!(listener_id, id); - // assert!( - // matches!(listen_addr.iter().next(), Some(Protocol::Ip4(a)) if !a.is_unspecified()) - // ); - // assert!( - // matches!(listen_addr.iter().nth(1), Some(Protocol::Udp(port)) if port != 0) - // ); - // assert!(matches!(listen_addr.iter().nth(2), Some(Protocol::QuicV1))); - // } - // e => panic!("Unexpected event: {e:?}"), - // } - // assert!(transport.remove_listener(id), "Expect listener to exist."); - // match poll_fn(|cx| Pin::new(&mut transport).as_mut().poll(cx)).await { - // TransportEvent::ListenerClosed { - // listener_id, - // reason: Ok(()), - // } => { - // assert_eq!(listener_id, id); - // } - // e => panic!("Unexpected event: {e:?}"), - // } - // // Poll once again so that the listener has the chance to return `Poll::Ready(None)` and - // // be removed from the list of listeners. - // assert!(poll_fn(|cx| Pin::new(&mut transport).as_mut().poll(cx)) - // .now_or_never() - // .is_none()); - // assert!(transport.listeners.is_empty()); - - // // Check that the [`Driver`] has shut down. - // Delay::new(Duration::from_millis(10)).await; - // poll_fn(|cx| { - // assert!(channel.try_send(ToEndpoint::Decoupled, cx).is_err()); - // Poll::Ready(()) - // }) - // .await; - // } - // } - - - // #[cfg(feature = "tokio")] - // #[tokio::test] - // async fn test_dialer_drop() { - // let keypair = libp2p_core::identity::Keypair::generate_ed25519(); - // let config = Config::new(&keypair); - // let mut transport = crate::tokio::Transport::new(config); - - // let _dial = transport - // .dial("/ip4/123.45.67.8/udp/1234/quic-v1".parse().unwrap()) - // .unwrap(); - - // // Expect a dialer and its background task to exist. - // let mut channel = transport - // .dialer - // .get(&SocketFamily::Ipv4) - // .unwrap() - // .endpoint_channel - // .clone(); - // assert!(!transport.dialer.contains_key(&SocketFamily::Ipv6)); - - // // Send dummy dial to check that the endpoint driver is running. - // poll_fn(|cx| { - // let (tx, _) = oneshot::channel(); - // let _ = channel - // .try_send( - // ToEndpoint::Dial { - // addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0), - // result: tx, - // version: ProtocolVersion::V1, - // }, - // cx, - // ) - // .unwrap(); - // Poll::Ready(()) - // }) - // .await; - - // // Start listening so that the dialer and driver are dropped. - // let _ = transport - // .listen_on("/ip4/0.0.0.0/udp/0/quic-v1".parse().unwrap()) - // .unwrap(); - // assert!(!transport.dialer.contains_key(&SocketFamily::Ipv4)); - - // // Check that the [`Driver`] has shut down. - // Delay::new(Duration::from_millis(10)).await; - // poll_fn(|cx| { - // assert!(channel.try_send(ToEndpoint::Decoupled, cx).is_err()); - // Poll::Ready(()) - // }) - // .await; - // } + #[cfg(feature = "async-std")] + #[async_std::test] + async fn test_close_listener() { + let keypair = libp2p_core::identity::Keypair::generate_ed25519(); + let config = Config::new(&keypair); + let mut transport = crate::async_std::Transport::new(config); + assert!(poll_fn(|cx| Pin::new(&mut transport).as_mut().poll(cx)) + .now_or_never() + .is_none()); + + // Run test twice to check that there is no unexpected behaviour if `Transport.listener` + // is temporarily empty. + for _ in 0..2 { + let id = transport + .listen_on("/ip4/0.0.0.0/udp/0/quic-v1".parse().unwrap()) + .unwrap(); + + match poll_fn(|cx| Pin::new(&mut transport).as_mut().poll(cx)).await { + TransportEvent::NewAddress { + listener_id, + listen_addr, + } => { + assert_eq!(listener_id, id); + assert!( + matches!(listen_addr.iter().next(), Some(Protocol::Ip4(a)) if !a.is_unspecified()) + ); + assert!( + matches!(listen_addr.iter().nth(1), Some(Protocol::Udp(port)) if port != 0) + ); + assert!(matches!(listen_addr.iter().nth(2), Some(Protocol::QuicV1))); + } + e => panic!("Unexpected event: {e:?}"), + } + assert!(transport.remove_listener(id), "Expect listener to exist."); + match poll_fn(|cx| Pin::new(&mut transport).as_mut().poll(cx)).await { + TransportEvent::ListenerClosed { + listener_id, + reason: Ok(()), + } => { + assert_eq!(listener_id, id); + } + e => panic!("Unexpected event: {e:?}"), + } + // Poll once again so that the listener has the chance to return `Poll::Ready(None)` and + // be removed from the list of listeners. + assert!(poll_fn(|cx| Pin::new(&mut transport).as_mut().poll(cx)) + .now_or_never() + .is_none()); + assert!(transport.listeners.is_empty()); + } + } + + #[cfg(feature = "tokio")] + #[tokio::test] + async fn test_dialer_drop() { + let keypair = libp2p_core::identity::Keypair::generate_ed25519(); + let config = Config::new(&keypair); + let mut transport = crate::tokio::Transport::new(config); + + let _dial = transport + .dial("/ip4/123.45.67.8/udp/1234/quic-v1".parse().unwrap()) + .unwrap(); + + assert!(transport.dialer.contains_key(&SocketFamily::Ipv4)); + assert!(!transport.dialer.contains_key(&SocketFamily::Ipv6)); + + // Start listening so that the dialer and driver are dropped. + let _ = transport + .listen_on("/ip4/0.0.0.0/udp/0/quic-v1".parse().unwrap()) + .unwrap(); + assert!(!transport.dialer.contains_key(&SocketFamily::Ipv4)); + } } From 7e788b64e5cdde400c126280d896daaa1f341d00 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 14 Feb 2023 19:58:54 +0000 Subject: [PATCH 17/60] Rename endpoint.rs -> config.rs --- transports/quic/src/{endpoint.rs => config.rs} | 0 transports/quic/src/lib.rs | 4 ++-- transports/quic/src/transport.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename transports/quic/src/{endpoint.rs => config.rs} (100%) diff --git a/transports/quic/src/endpoint.rs b/transports/quic/src/config.rs similarity index 100% rename from transports/quic/src/endpoint.rs rename to transports/quic/src/config.rs diff --git a/transports/quic/src/lib.rs b/transports/quic/src/lib.rs index c3660fe7b46..9745b3851a2 100644 --- a/transports/quic/src/lib.rs +++ b/transports/quic/src/lib.rs @@ -58,12 +58,12 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] mod connection; -mod endpoint; +mod config; mod provider; mod transport; pub use connection::{Connecting, Connection, Substream}; -pub use endpoint::Config; +pub use config::Config; #[cfg(feature = "async-std")] pub use provider::async_std; #[cfg(feature = "tokio")] diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index da1b6b2280c..e9bcc679688 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::endpoint::{Config, QuinnConfig}; +use crate::config::{Config, QuinnConfig}; use crate::provider::Provider; use crate::{Connecting, ConnectError, Connection, Error}; From 953ce8344f53ea956e5e4146d65783d7e02e30fd Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 15 Feb 2023 12:06:59 +0000 Subject: [PATCH 18/60] Fix doc links --- transports/quic/src/config.rs | 8 ++++---- transports/quic/src/transport.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/transports/quic/src/config.rs b/transports/quic/src/config.rs index 6bc0ed8fcc0..1c07af8207f 100644 --- a/transports/quic/src/config.rs +++ b/transports/quic/src/config.rs @@ -36,7 +36,7 @@ pub struct Config { /// Must be set lower than the idle_timeout of both /// peers to be effective. /// - /// See [`quinn_proto::TransportConfig::keep_alive_interval`] for more + /// See [`quinn::TransportConfig::keep_alive_interval`] for more /// info. pub keep_alive_interval: Duration, /// Maximum number of incoming bidirectional streams that may be open @@ -60,9 +60,9 @@ pub struct Config { /// As client the version is chosen based on the remote's address. pub support_draft_29: bool, - /// TLS client config for the inner [`quinn_proto::ClientConfig`]. + /// TLS client config for the inner [`quinn::ClientConfig`]. client_tls_config: Arc, - /// TLS server config for the inner [`quinn_proto::ServerConfig`]. + /// TLS server config for the inner [`quinn::ServerConfig`]. server_tls_config: Arc, } @@ -87,7 +87,7 @@ impl Config { } } -/// Represents the inner configuration for [`quinn_proto`]. +/// Represents the inner configuration for [`quinn`]. #[derive(Debug, Clone)] pub struct QuinnConfig { pub(crate) client_config: quinn::ClientConfig, diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index e9bcc679688..87051396ad5 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -59,7 +59,7 @@ use std::{ /// See . #[derive(Debug)] pub struct GenTransport { - /// Config for the inner [`quinn_proto`] structs. + /// Config for the inner [`quinn`] structs. quinn_config: QuinnConfig, /// Timeout for the [`Connecting`] future. handshake_timeout: Duration, From df0f78931be07772ade1d74205778824f0e6847f Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 15 Feb 2023 12:07:45 +0000 Subject: [PATCH 19/60] cargo fmt --- transports/quic/src/config.rs | 5 +--- transports/quic/src/connection/connecting.rs | 5 +++- transports/quic/src/lib.rs | 4 +-- transports/quic/src/transport.rs | 30 +++++++++++--------- 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/transports/quic/src/config.rs b/transports/quic/src/config.rs index 1c07af8207f..79cc86f2309 100644 --- a/transports/quic/src/config.rs +++ b/transports/quic/src/config.rs @@ -19,10 +19,7 @@ // DEALINGS IN THE SOFTWARE. use quinn::VarInt; -use std::{ - sync::Arc, - time::Duration, -}; +use std::{sync::Arc, time::Duration}; /// Config for the transport. #[derive(Clone)] diff --git a/transports/quic/src/connection/connecting.rs b/transports/quic/src/connection/connecting.rs index ec581baaca4..32324b766d2 100644 --- a/transports/quic/src/connection/connecting.rs +++ b/transports/quic/src/connection/connecting.rs @@ -22,7 +22,10 @@ use crate::{Connection, ConnectionError, Error}; -use futures::{prelude::*, future::{Either, select, Select}}; +use futures::{ + future::{select, Either, Select}, + prelude::*, +}; use futures_timer::Delay; use libp2p_core::PeerId; use std::{ diff --git a/transports/quic/src/lib.rs b/transports/quic/src/lib.rs index 9745b3851a2..957c5476f82 100644 --- a/transports/quic/src/lib.rs +++ b/transports/quic/src/lib.rs @@ -57,13 +57,13 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -mod connection; mod config; +mod connection; mod provider; mod transport; -pub use connection::{Connecting, Connection, Substream}; pub use config::Config; +pub use connection::{Connecting, Connection, Substream}; #[cfg(feature = "async-std")] pub use provider::async_std; #[cfg(feature = "tokio")] diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index 87051396ad5..164a3048fb6 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -20,7 +20,7 @@ use crate::config::{Config, QuinnConfig}; use crate::provider::Provider; -use crate::{Connecting, ConnectError, Connection, Error}; +use crate::{ConnectError, Connecting, Connection, Error}; use futures::future::BoxFuture; use futures::ready; @@ -89,7 +89,11 @@ impl GenTransport

{ } } /// Create a new [`quinn::Endpoint`] with the given configs. - fn new_endpoint(endpoint_config: quinn::EndpointConfig, server_config: Option, socket_addr: SocketAddr) -> Result { + fn new_endpoint( + endpoint_config: quinn::EndpointConfig, + server_config: Option, + socket_addr: SocketAddr, + ) -> Result { let socket = UdpSocket::bind(socket_addr)?; let endpoint = quinn::Endpoint::new(endpoint_config, server_config, socket, P::runtime())?; Ok(endpoint) @@ -185,7 +189,8 @@ impl Transport for GenTransport

{ SocketFamily::Ipv6 => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), }; let endpoint_config = self.quinn_config.endpoint_config.clone(); - let endpoint = Self::new_endpoint(endpoint_config, None, listen_socket_addr)?; + let endpoint = + Self::new_endpoint(endpoint_config, None, listen_socket_addr)?; vacant.insert(endpoint.clone()); endpoint @@ -211,7 +216,8 @@ impl Transport for GenTransport

{ // This `"l"` seems necessary because an empty string is an invalid domain // name. While we don't use domain names, the underlying rustls library // is based upon the assumption that we do. - let connecting = endpoint.connect_with(client_config, socket_addr, "l") + let connecting = endpoint + .connect_with(client_config, socket_addr, "l") .map_err(ConnectError)?; Connecting::new(connecting, handshake_timeout).await })) @@ -349,11 +355,9 @@ impl Listener

{ loop { match ready!(P::poll_if_event(if_watcher, cx)) { Ok(IfEvent::Up(inet)) => { - if let Some(listen_addr) = ip_to_listenaddr( - &endpoint_addr, - inet.addr(), - self.version, - ) { + if let Some(listen_addr) = + ip_to_listenaddr(&endpoint_addr, inet.addr(), self.version) + { log::debug!("New listen address: {}", listen_addr); return Poll::Ready(TransportEvent::NewAddress { listener_id: self.listener_id, @@ -362,11 +366,9 @@ impl Listener

{ } } Ok(IfEvent::Down(inet)) => { - if let Some(listen_addr) = ip_to_listenaddr( - &endpoint_addr, - inet.addr(), - self.version, - ) { + if let Some(listen_addr) = + ip_to_listenaddr(&endpoint_addr, inet.addr(), self.version) + { log::debug!("Expired listen address: {}", listen_addr); return Poll::Ready(TransportEvent::AddressExpired { listener_id: self.listener_id, From ab1c047924f6fe94441cfddc96ce66c5e5f91c2c Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 7 Mar 2023 04:45:00 +0000 Subject: [PATCH 20/60] Review fixes --- transports/quic/src/connection.rs | 8 ++++---- transports/quic/src/transport.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/transports/quic/src/connection.rs b/transports/quic/src/connection.rs index 5dd2570f11a..a2c2d613502 100644 --- a/transports/quic/src/connection.rs +++ b/transports/quic/src/connection.rs @@ -50,9 +50,9 @@ impl Connection { /// its methods has ever been called. Failure to comply might lead to logic errors and panics. fn new(connection: quinn::Connection) -> Self { let connection_c = connection.clone(); - let incoming = Box::pin(async move { connection_c.accept_bi().await }); + let incoming = async move { connection_c.accept_bi().await }.boxed(); let connection_c = connection.clone(); - let outgoing = Box::pin(async move { connection_c.open_bi().await }); + let outgoing = async move { connection_c.open_bi().await }.boxed(); Self { connection, incoming, @@ -73,7 +73,7 @@ impl StreamMuxer for Connection { let (send, recv) = futures::ready!(this.incoming.poll_unpin(cx))?; let connection = this.connection.clone(); - this.incoming = Box::pin(async move { connection.accept_bi().await }); + this.incoming = async move { connection.accept_bi().await }.boxed(); let substream = Substream::new(send, recv); Poll::Ready(Ok(substream)) } @@ -86,7 +86,7 @@ impl StreamMuxer for Connection { let (send, recv) = futures::ready!(this.outgoing.poll_unpin(cx))?; let connection = this.connection.clone(); - this.outgoing = Box::pin(async move { connection.open_bi().await }); + this.outgoing = async move { connection.open_bi().await }.boxed(); let substream = Substream::new(send, recv); Poll::Ready(Ok(substream)) } diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index 164a3048fb6..b165130b8e2 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -307,7 +307,7 @@ impl Listener

{ } let endpoint_c = endpoint.clone(); - let accept = Box::pin(async move { endpoint_c.accept().await }); + let accept = async move { endpoint_c.accept().await }.boxed(); Ok(Listener { endpoint, @@ -404,7 +404,7 @@ impl Stream for Listener

{ match self.accept.poll_unpin(cx) { Poll::Ready(Some(connecting)) => { let endpoint = self.endpoint.clone(); - self.accept = Box::pin(async move { endpoint.accept().await }); + self.accept = async move { endpoint.accept().await }.boxed(); let local_addr = socketaddr_to_multiaddr(&self.socket_addr(), self.version); let send_back_addr = From 54606bf56397bb99c0abdf7ea7cace680d2e274e Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 7 Mar 2023 04:51:40 +0000 Subject: [PATCH 21/60] Review fixes --- transports/quic/src/connection.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/transports/quic/src/connection.rs b/transports/quic/src/connection.rs index a2c2d613502..4aa5476c5d7 100644 --- a/transports/quic/src/connection.rs +++ b/transports/quic/src/connection.rs @@ -24,6 +24,8 @@ mod substream; pub use connecting::Connecting; pub use substream::Substream; +use crate::{ConnectionError, Error}; + use futures::{future::BoxFuture, FutureExt}; use libp2p_core::muxing::{StreamMuxer, StreamMuxerEvent}; use std::{ @@ -63,7 +65,7 @@ impl Connection { impl StreamMuxer for Connection { type Substream = Substream; - type Error = quinn::ConnectionError; + type Error = Error; fn poll_inbound( self: Pin<&mut Self>, @@ -71,7 +73,8 @@ impl StreamMuxer for Connection { ) -> Poll> { let this = self.get_mut(); - let (send, recv) = futures::ready!(this.incoming.poll_unpin(cx))?; + let (send, recv) = + futures::ready!(this.incoming.poll_unpin(cx)).map_err(ConnectionError)?; let connection = this.connection.clone(); this.incoming = async move { connection.accept_bi().await }.boxed(); let substream = Substream::new(send, recv); @@ -84,7 +87,8 @@ impl StreamMuxer for Connection { ) -> Poll> { let this = self.get_mut(); - let (send, recv) = futures::ready!(this.outgoing.poll_unpin(cx))?; + let (send, recv) = + futures::ready!(this.outgoing.poll_unpin(cx)).map_err(ConnectionError)?; let connection = this.connection.clone(); this.outgoing = async move { connection.open_bi().await }.boxed(); let substream = Substream::new(send, recv); From 808a6b48d2be0c45307f171800dfb3a89232ecbb Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 31 Mar 2023 19:57:17 +0000 Subject: [PATCH 22/60] Make incoming and outgoing futures in connection an Option<_> --- transports/quic/src/connection.rs | 36 +++++++++++++++++-------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/transports/quic/src/connection.rs b/transports/quic/src/connection.rs index 4aa5476c5d7..f31ae0f3d88 100644 --- a/transports/quic/src/connection.rs +++ b/transports/quic/src/connection.rs @@ -38,11 +38,13 @@ pub struct Connection { /// Underlying connection. connection: quinn::Connection, /// Future for accepting a new incoming bidirectional stream. - incoming: + incoming: Option< BoxFuture<'static, Result<(quinn::SendStream, quinn::RecvStream), quinn::ConnectionError>>, + >, /// Future for opening a new outgoing a new bidirectional stream. - outgoing: + outgoing: Option< BoxFuture<'static, Result<(quinn::SendStream, quinn::RecvStream), quinn::ConnectionError>>, + >, } impl Connection { @@ -51,14 +53,10 @@ impl Connection { /// This function assumes that the [`quinn::Connection`] is completely fresh and none of /// its methods has ever been called. Failure to comply might lead to logic errors and panics. fn new(connection: quinn::Connection) -> Self { - let connection_c = connection.clone(); - let incoming = async move { connection_c.accept_bi().await }.boxed(); - let connection_c = connection.clone(); - let outgoing = async move { connection_c.open_bi().await }.boxed(); Self { connection, - incoming, - outgoing, + incoming: None, + outgoing: None, } } } @@ -73,10 +71,13 @@ impl StreamMuxer for Connection { ) -> Poll> { let this = self.get_mut(); - let (send, recv) = - futures::ready!(this.incoming.poll_unpin(cx)).map_err(ConnectionError)?; - let connection = this.connection.clone(); - this.incoming = async move { connection.accept_bi().await }.boxed(); + let incoming = this.incoming.get_or_insert_with(|| { + let connection = this.connection.clone(); + async move { connection.accept_bi().await }.boxed() + }); + + let (send, recv) = futures::ready!(incoming.poll_unpin(cx)).map_err(ConnectionError)?; + this.incoming.take(); let substream = Substream::new(send, recv); Poll::Ready(Ok(substream)) } @@ -87,10 +88,13 @@ impl StreamMuxer for Connection { ) -> Poll> { let this = self.get_mut(); - let (send, recv) = - futures::ready!(this.outgoing.poll_unpin(cx)).map_err(ConnectionError)?; - let connection = this.connection.clone(); - this.outgoing = async move { connection.open_bi().await }.boxed(); + let outgoing = this.outgoing.get_or_insert_with(|| { + let connection = this.connection.clone(); + async move { connection.open_bi().await }.boxed() + }); + + let (send, recv) = futures::ready!(outgoing.poll_unpin(cx)).map_err(ConnectionError)?; + this.outgoing.take(); let substream = Substream::new(send, recv); Poll::Ready(Ok(substream)) } From ce30bcb1255167994fcc2715302a5a07d9ed2420 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 31 Mar 2023 22:58:19 +0300 Subject: [PATCH 23/60] Update transports/quic/src/connection.rs Co-authored-by: Max Inden --- transports/quic/src/connection.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transports/quic/src/connection.rs b/transports/quic/src/connection.rs index f31ae0f3d88..4bda971a592 100644 --- a/transports/quic/src/connection.rs +++ b/transports/quic/src/connection.rs @@ -41,7 +41,7 @@ pub struct Connection { incoming: Option< BoxFuture<'static, Result<(quinn::SendStream, quinn::RecvStream), quinn::ConnectionError>>, >, - /// Future for opening a new outgoing a new bidirectional stream. + /// Future for opening a new outgoing bidirectional stream. outgoing: Option< BoxFuture<'static, Result<(quinn::SendStream, quinn::RecvStream), quinn::ConnectionError>>, >, From 11bf9601d19c7305f5b94a5d106cba7447aa38a0 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Fri, 31 Mar 2023 20:19:59 +0000 Subject: [PATCH 24/60] Refactor poll_close for Substream --- transports/quic/src/connection/substream.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/transports/quic/src/connection/substream.rs b/transports/quic/src/connection/substream.rs index e1a0e0fd9f0..6bdf126105e 100644 --- a/transports/quic/src/connection/substream.rs +++ b/transports/quic/src/connection/substream.rs @@ -32,7 +32,6 @@ pub struct Substream { send: quinn::SendStream, /// A receive part of the stream recv: quinn::RecvStream, - closed: bool, // TODO check whether this is necessary } impl Substream { @@ -40,7 +39,6 @@ impl Substream { Self { send, recv, - closed: false, } } } @@ -66,14 +64,6 @@ impl AsyncWrite for Substream { fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let this = self.get_mut(); - if this.closed { - // For some reason poll_close needs to be 'fuse'able - return Poll::Ready(Ok(())); - } - let close_result = AsyncWrite::poll_close(Pin::new(&mut this.send), cx); - if close_result.is_ready() { - this.closed = true; - } - close_result + AsyncWrite::poll_close(Pin::new(&mut this.send), cx) } } From 2f6e719194cec4e674855f452b687743208c9a59 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 11 Apr 2023 10:04:18 +0000 Subject: [PATCH 25/60] cargo fmt --- transports/quic/src/connection/substream.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/transports/quic/src/connection/substream.rs b/transports/quic/src/connection/substream.rs index 6bdf126105e..80438bf0c65 100644 --- a/transports/quic/src/connection/substream.rs +++ b/transports/quic/src/connection/substream.rs @@ -36,10 +36,7 @@ pub struct Substream { impl Substream { pub(super) fn new(send: quinn::SendStream, recv: quinn::RecvStream) -> Self { - Self { - send, - recv, - } + Self { send, recv } } } From 2a93d21cb4f712ad06544c2f626f9738a734763b Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 19 Apr 2023 07:20:06 +0000 Subject: [PATCH 26/60] Remove quinn structs from public API --- transports/quic/src/provider.rs | 11 +++++++-- transports/quic/src/provider/async_std.rs | 5 ++-- transports/quic/src/provider/tokio.rs | 5 ++-- transports/quic/src/transport.rs | 29 +++++++++++++++++++---- 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/transports/quic/src/provider.rs b/transports/quic/src/provider.rs index 8b616257b79..db1002f3ef5 100644 --- a/transports/quic/src/provider.rs +++ b/transports/quic/src/provider.rs @@ -30,13 +30,20 @@ pub mod async_std; #[cfg(feature = "tokio")] pub mod tokio; +pub enum Runtime { + #[cfg(feature = "tokio")] + Tokio(quinn::TokioRuntime), + #[cfg(feature = "async-std")] + AsyncStd(quinn::AsyncStdRuntime), + Dummy, +} + /// Provider for a corresponding quinn runtime and spawning tasks. pub trait Provider: Unpin + Send + Sized + 'static { type IfWatcher: Unpin + Send; - type Runtime: quinn::Runtime; /// Run the corresponding runtime - fn runtime() -> Self::Runtime; + fn runtime() -> Runtime; /// Run the given future in the background until it ends. /// diff --git a/transports/quic/src/provider/async_std.rs b/transports/quic/src/provider/async_std.rs index d5dc2cdf372..b08f1033777 100644 --- a/transports/quic/src/provider/async_std.rs +++ b/transports/quic/src/provider/async_std.rs @@ -35,10 +35,9 @@ pub struct Provider; impl super::Provider for Provider { type IfWatcher = if_watch::smol::IfWatcher; - type Runtime = quinn::AsyncStdRuntime; - fn runtime() -> Self::Runtime { - quinn::AsyncStdRuntime + fn runtime() -> super::Runtime { + super::Runtime::AsyncStd(quinn::AsyncStdRuntime) } fn spawn(future: impl Future + Send + 'static) { diff --git a/transports/quic/src/provider/tokio.rs b/transports/quic/src/provider/tokio.rs index adeb0906183..1016c89d6e7 100644 --- a/transports/quic/src/provider/tokio.rs +++ b/transports/quic/src/provider/tokio.rs @@ -34,10 +34,9 @@ pub struct Provider; impl super::Provider for Provider { type IfWatcher = if_watch::tokio::IfWatcher; - type Runtime = quinn::TokioRuntime; - fn runtime() -> Self::Runtime { - quinn::TokioRuntime + fn runtime() -> super::Runtime { + super::Runtime::Tokio(quinn::TokioRuntime) } fn spawn(future: impl Future + Send + 'static) { diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index 76f8da5ff5c..f718c4b5fee 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -39,7 +39,7 @@ use std::collections::hash_map::{DefaultHasher, Entry}; use std::collections::HashMap; use std::fmt; use std::hash::{Hash, Hasher}; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, UdpSocket}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::time::Duration; use std::{ net::SocketAddr, @@ -95,9 +95,30 @@ impl GenTransport

{ server_config: Option, socket_addr: SocketAddr, ) -> Result { - let socket = UdpSocket::bind(socket_addr)?; - let endpoint = quinn::Endpoint::new(endpoint_config, server_config, socket, P::runtime())?; - Ok(endpoint) + use crate::provider::Runtime; + match P::runtime() { + #[cfg(feature = "tokio")] + Runtime::Tokio(runtime) => { + let socket = std::net::UdpSocket::bind(socket_addr)?; + let endpoint = + quinn::Endpoint::new(endpoint_config, server_config, socket, runtime)?; + Ok(endpoint) + } + #[cfg(feature = "async-std")] + Runtime::AsyncStd(runtime) => { + let socket = std::net::UdpSocket::bind(socket_addr)?; + let endpoint = + quinn::Endpoint::new(endpoint_config, server_config, socket, runtime)?; + Ok(endpoint) + } + Runtime::Dummy => { + let _ = endpoint_config; + let _ = server_config; + let _ = socket_addr; + let err = std::io::Error::new(std::io::ErrorKind::Other, "no async runtime found"); + Err(Error::Io(err)) + } + } } } From c357f7356fc03da70bf881fc5b2a380fb0037e15 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 3 May 2023 07:48:00 +0000 Subject: [PATCH 27/60] Remove quinn structs from public Runtime API --- transports/quic/src/provider.rs | 4 ++-- transports/quic/src/provider/async_std.rs | 2 +- transports/quic/src/provider/tokio.rs | 2 +- transports/quic/src/transport.rs | 6 ++++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/transports/quic/src/provider.rs b/transports/quic/src/provider.rs index db1002f3ef5..5f740b74ecc 100644 --- a/transports/quic/src/provider.rs +++ b/transports/quic/src/provider.rs @@ -32,9 +32,9 @@ pub mod tokio; pub enum Runtime { #[cfg(feature = "tokio")] - Tokio(quinn::TokioRuntime), + Tokio, #[cfg(feature = "async-std")] - AsyncStd(quinn::AsyncStdRuntime), + AsyncStd, Dummy, } diff --git a/transports/quic/src/provider/async_std.rs b/transports/quic/src/provider/async_std.rs index b08f1033777..8bf37036b6f 100644 --- a/transports/quic/src/provider/async_std.rs +++ b/transports/quic/src/provider/async_std.rs @@ -37,7 +37,7 @@ impl super::Provider for Provider { type IfWatcher = if_watch::smol::IfWatcher; fn runtime() -> super::Runtime { - super::Runtime::AsyncStd(quinn::AsyncStdRuntime) + super::Runtime::AsyncStd } fn spawn(future: impl Future + Send + 'static) { diff --git a/transports/quic/src/provider/tokio.rs b/transports/quic/src/provider/tokio.rs index 1016c89d6e7..5be56e51d7d 100644 --- a/transports/quic/src/provider/tokio.rs +++ b/transports/quic/src/provider/tokio.rs @@ -36,7 +36,7 @@ impl super::Provider for Provider { type IfWatcher = if_watch::tokio::IfWatcher; fn runtime() -> super::Runtime { - super::Runtime::Tokio(quinn::TokioRuntime) + super::Runtime::Tokio } fn spawn(future: impl Future + Send + 'static) { diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index 90a910508f0..90d289cb232 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -98,14 +98,16 @@ impl GenTransport

{ use crate::provider::Runtime; match P::runtime() { #[cfg(feature = "tokio")] - Runtime::Tokio(runtime) => { + Runtime::Tokio => { + let runtime = quinn::TokioRuntime; let socket = std::net::UdpSocket::bind(socket_addr)?; let endpoint = quinn::Endpoint::new(endpoint_config, server_config, socket, runtime)?; Ok(endpoint) } #[cfg(feature = "async-std")] - Runtime::AsyncStd(runtime) => { + Runtime::AsyncStd => { + let runtime = quinn::AsyncStdRuntime; let socket = std::net::UdpSocket::bind(socket_addr)?; let endpoint = quinn::Endpoint::new(endpoint_config, server_config, socket, runtime)?; From 2a1948c36961f72ddba448a213d162b7269b092c Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 3 May 2023 08:03:37 +0000 Subject: [PATCH 28/60] Remove thiserror::from impl for error types --- transports/quic/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/transports/quic/src/lib.rs b/transports/quic/src/lib.rs index 599639f7fb2..7139552d96c 100644 --- a/transports/quic/src/lib.rs +++ b/transports/quic/src/lib.rs @@ -99,9 +99,9 @@ pub enum Error { /// Dialing a remote peer failed. #[derive(Debug, thiserror::Error)] #[error(transparent)] -pub struct ConnectError(#[from] quinn::ConnectError); +pub struct ConnectError(quinn::ConnectError); /// Error on an established [`Connection`]. #[derive(Debug, thiserror::Error)] #[error(transparent)] -pub struct ConnectionError(#[from] quinn::ConnectionError); +pub struct ConnectionError(quinn::ConnectionError); From ae0b427aa30988d89d3b71302a78c140336afd4b Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Mon, 22 May 2023 11:16:33 +0000 Subject: [PATCH 29/60] Fix merge --- transports/quic/src/endpoint.rs | 667 -------------------------------- 1 file changed, 667 deletions(-) delete mode 100644 transports/quic/src/endpoint.rs diff --git a/transports/quic/src/endpoint.rs b/transports/quic/src/endpoint.rs deleted file mode 100644 index cef062a0d7e..00000000000 --- a/transports/quic/src/endpoint.rs +++ /dev/null @@ -1,667 +0,0 @@ -// Copyright 2017-2020 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -use crate::{ - provider::Provider, - transport::{ProtocolVersion, SocketFamily}, - ConnectError, Connection, Error, -}; - -use bytes::BytesMut; -use futures::{ - channel::{mpsc, oneshot}, - prelude::*, -}; -use quinn_proto::VarInt; -use std::{ - collections::HashMap, - net::{Ipv4Addr, Ipv6Addr, SocketAddr}, - ops::ControlFlow, - pin::Pin, - sync::Arc, - task::{Context, Poll}, - time::{Duration, Instant}, -}; - -// The `Driver` drops packets if the channel to the connection -// or transport is full. -// Set capacity 10 to avoid unnecessary packet drops if the receiver -// is only very briefly busy, but not buffer a large amount of packets -// if it is blocked longer. -const CHANNEL_CAPACITY: usize = 10; - -/// Config for the transport. -#[derive(Clone)] -pub struct Config { - /// Timeout for the initial handshake when establishing a connection. - /// The actual timeout is the minimum of this an the [`Config::max_idle_timeout`]. - pub handshake_timeout: Duration, - /// Maximum duration of inactivity in ms to accept before timing out the connection. - pub max_idle_timeout: u32, - /// Period of inactivity before sending a keep-alive packet. - /// Must be set lower than the idle_timeout of both - /// peers to be effective. - /// - /// See [`quinn_proto::TransportConfig::keep_alive_interval`] for more - /// info. - pub keep_alive_interval: Duration, - /// Maximum number of incoming bidirectional streams that may be open - /// concurrently by the remote peer. - pub max_concurrent_stream_limit: u32, - - /// Max unacknowledged data in bytes that may be send on a single stream. - pub max_stream_data: u32, - - /// Max unacknowledged data in bytes that may be send in total on all streams - /// of a connection. - pub max_connection_data: u32, - - /// Support QUIC version draft-29 for dialing and listening. - /// - /// Per default only QUIC Version 1 / [`libp2p_core::multiaddr::Protocol::QuicV1`] - /// is supported. - /// - /// If support for draft-29 is enabled servers support draft-29 and version 1 on all - /// QUIC listening addresses. - /// As client the version is chosen based on the remote's address. - pub support_draft_29: bool, - - /// TLS client config for the inner [`quinn_proto::ClientConfig`]. - client_tls_config: Arc, - /// TLS server config for the inner [`quinn_proto::ServerConfig`]. - server_tls_config: Arc, -} - -impl Config { - /// Creates a new configuration object with default values. - pub fn new(keypair: &libp2p_identity::Keypair) -> Self { - let client_tls_config = Arc::new(libp2p_tls::make_client_config(keypair, None).unwrap()); - let server_tls_config = Arc::new(libp2p_tls::make_server_config(keypair).unwrap()); - Self { - client_tls_config, - server_tls_config, - support_draft_29: false, - handshake_timeout: Duration::from_secs(5), - max_idle_timeout: 30 * 1000, - max_concurrent_stream_limit: 256, - keep_alive_interval: Duration::from_secs(15), - max_connection_data: 15_000_000, - - // Ensure that one stream is not consuming the whole connection. - max_stream_data: 10_000_000, - } - } -} - -/// Represents the inner configuration for [`quinn_proto`]. -#[derive(Debug, Clone)] -pub(crate) struct QuinnConfig { - client_config: quinn_proto::ClientConfig, - server_config: Arc, - endpoint_config: Arc, -} - -impl From for QuinnConfig { - fn from(config: Config) -> QuinnConfig { - let Config { - client_tls_config, - server_tls_config, - max_idle_timeout, - max_concurrent_stream_limit, - keep_alive_interval, - max_connection_data, - max_stream_data, - support_draft_29, - handshake_timeout: _, - } = config; - let mut transport = quinn_proto::TransportConfig::default(); - // Disable uni-directional streams. - transport.max_concurrent_uni_streams(0u32.into()); - transport.max_concurrent_bidi_streams(max_concurrent_stream_limit.into()); - // Disable datagrams. - transport.datagram_receive_buffer_size(None); - transport.keep_alive_interval(Some(keep_alive_interval)); - transport.max_idle_timeout(Some(VarInt::from_u32(max_idle_timeout).into())); - transport.allow_spin(false); - transport.stream_receive_window(max_stream_data.into()); - transport.receive_window(max_connection_data.into()); - let transport = Arc::new(transport); - - let mut server_config = quinn_proto::ServerConfig::with_crypto(server_tls_config); - server_config.transport = Arc::clone(&transport); - // Disables connection migration. - // Long-term this should be enabled, however we then need to handle address change - // on connections in the `Connection`. - server_config.migration(false); - - let mut client_config = quinn_proto::ClientConfig::new(client_tls_config); - client_config.transport_config(transport); - - let mut endpoint_config = quinn_proto::EndpointConfig::default(); - if !support_draft_29 { - endpoint_config.supported_versions(vec![1]); - } - - QuinnConfig { - client_config, - server_config: Arc::new(server_config), - endpoint_config: Arc::new(endpoint_config), - } - } -} - -/// Channel used to send commands to the [`Driver`]. -#[derive(Debug, Clone)] -pub(crate) struct Channel { - /// Channel to the background of the endpoint. - to_endpoint: mpsc::Sender, - /// Address that the socket is bound to. - /// Note: this may be a wildcard ip address. - socket_addr: SocketAddr, -} - -impl Channel { - /// Builds a new endpoint that is listening on the [`SocketAddr`]. - pub(crate) fn new_bidirectional( - quinn_config: QuinnConfig, - socket_addr: SocketAddr, - ) -> Result<(Self, mpsc::Receiver), Error> { - // Channel for forwarding new inbound connections to the listener. - let (new_connections_tx, new_connections_rx) = mpsc::channel(CHANNEL_CAPACITY); - let endpoint = Self::new::

(quinn_config, socket_addr, Some(new_connections_tx))?; - Ok((endpoint, new_connections_rx)) - } - - /// Builds a new endpoint that only supports outbound connections. - pub(crate) fn new_dialer( - quinn_config: QuinnConfig, - socket_family: SocketFamily, - ) -> Result { - let socket_addr = match socket_family { - SocketFamily::Ipv4 => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), - SocketFamily::Ipv6 => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), - }; - Self::new::

(quinn_config, socket_addr, None) - } - - /// Spawn a new [`Driver`] that runs in the background. - fn new( - quinn_config: QuinnConfig, - socket_addr: SocketAddr, - new_connections: Option>, - ) -> Result { - let socket = std::net::UdpSocket::bind(socket_addr)?; - // NOT blocking, as per man:bind(2), as we pass an IP address. - socket.set_nonblocking(true)?; - // Capacity 0 to back-pressure the rest of the application if - // the udp socket is busy. - let (to_endpoint_tx, to_endpoint_rx) = mpsc::channel(0); - - let channel = Self { - to_endpoint: to_endpoint_tx, - socket_addr: socket.local_addr()?, - }; - - let server_config = new_connections - .is_some() - .then_some(quinn_config.server_config); - - let provider_socket = P::from_socket(socket)?; - - let driver = Driver::

::new( - quinn_config.endpoint_config, - quinn_config.client_config, - new_connections, - server_config, - channel.clone(), - provider_socket, - to_endpoint_rx, - ); - - // Drive the endpoint future in the background. - P::spawn(driver); - - Ok(channel) - } - - pub(crate) fn socket_addr(&self) -> &SocketAddr { - &self.socket_addr - } - - /// Try to send a message to the background task without blocking. - /// - /// This first polls the channel for capacity. - /// If the channel is full, the message is returned in `Ok(Err(_))` - /// and the context's waker is registered for wake-up. - /// - /// If the background task crashed `Err` is returned. - pub(crate) fn try_send( - &mut self, - to_endpoint: ToEndpoint, - cx: &mut Context<'_>, - ) -> Result, Disconnected> { - match self.to_endpoint.poll_ready_unpin(cx) { - Poll::Ready(Ok(())) => {} - Poll::Ready(Err(e)) => { - debug_assert!( - e.is_disconnected(), - "mpsc::Sender can only be disconnected when calling `poll_ready_unpin" - ); - - return Err(Disconnected {}); - } - Poll::Pending => return Ok(Err(to_endpoint)), - }; - - if let Err(e) = self.to_endpoint.start_send(to_endpoint) { - debug_assert!(e.is_disconnected(), "We called `Sink::poll_ready` so we are guaranteed to have a slot. If this fails, it means we are disconnected."); - - return Err(Disconnected {}); - } - - Ok(Ok(())) - } - - /// Send a message to inform the [`Driver`] about an - /// event caused by the owner of this [`Channel`] dropping. - /// This clones the sender to the endpoint to guarantee delivery. - /// This should *not* be called for regular messages. - pub(crate) fn send_on_drop(&mut self, to_endpoint: ToEndpoint) { - let _ = self.to_endpoint.clone().try_send(to_endpoint); - } -} - -#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)] -#[error("Background task disconnected")] -pub(crate) struct Disconnected {} -/// Message sent to the endpoint background task. -#[derive(Debug)] -pub(crate) enum ToEndpoint { - /// Instruct the [`quinn_proto::Endpoint`] to start connecting to the given address. - Dial { - /// UDP address to connect to. - addr: SocketAddr, - /// Version to dial the remote on. - version: ProtocolVersion, - /// Channel to return the result of the dialing to. - result: oneshot::Sender>, - }, - /// Send by a [`quinn_proto::Connection`] when the endpoint needs to process an event generated - /// by a connection. The event itself is opaque to us. Only `quinn_proto` knows what is in - /// there. - ProcessConnectionEvent { - connection_id: quinn_proto::ConnectionHandle, - event: quinn_proto::EndpointEvent, - }, - /// Instruct the endpoint to send a packet of data on its UDP socket. - SendUdpPacket(quinn_proto::Transmit), - /// The [`GenTransport`][crate::GenTransport] dialer or listener coupled to this endpoint - /// was dropped. - /// Once all pending connections are closed, the [`Driver`] should shut down. - Decoupled, -} - -/// Driver that runs in the background for as long as the endpoint is alive. Responsible for -/// processing messages and the UDP socket. -/// -/// # Behaviour -/// -/// This background task is responsible for the following: -/// -/// - Sending packets on the UDP socket. -/// - Receiving packets from the UDP socket and feed them to the [`quinn_proto::Endpoint`] state -/// machine. -/// - Transmitting events generated by the [`quinn_proto::Endpoint`] to the corresponding -/// [`crate::Connection`]. -/// - Receiving messages from the `rx` and processing the requested actions. This includes -/// UDP packets to send and events emitted by the [`crate::Connection`] objects. -/// - Sending new connections on `new_connection_tx`. -/// -/// When it comes to channels, there exists three main multi-producer-single-consumer channels -/// in play: -/// -/// - One channel, represented by `EndpointChannel::to_endpoint` and `Driver::rx`, -/// that communicates messages from [`Channel`] to the [`Driver`]. -/// - One channel for each existing connection that communicates messages from the -/// [`Driver` to that [`crate::Connection`]. -/// - One channel for the [`Driver`] to send newly-opened connections to. The receiving -/// side is processed by the [`GenTransport`][crate::GenTransport]. -/// -/// -/// ## Back-pressure -/// -/// ### If writing to the UDP socket is blocked -/// -/// In order to avoid an unbounded buffering of events, we prioritize sending data on the UDP -/// socket over everything else. Messages from the rest of the application sent through the -/// [`Channel`] are only processed if the UDP socket is ready so that we propagate back-pressure -/// in case of a busy socket. For connections, thus this eventually also back-pressures the -/// `AsyncWrite`on substreams. -/// -/// -/// ### Back-pressuring the remote if the application is busy -/// -/// If the channel to a connection is full because the connection is busy, inbound datagrams -/// for that connection are dropped so that the remote is backpressured. -/// The same applies for new connections if the transport is too busy to received it. -/// -/// -/// # Shutdown -/// -/// The background task shuts down if an [`ToEndpoint::Decoupled`] event was received and the -/// last active connection has drained. -#[derive(Debug)] -pub(crate) struct Driver { - // The actual QUIC state machine. - endpoint: quinn_proto::Endpoint, - // QuinnConfig for client connections. - client_config: quinn_proto::ClientConfig, - // Copy of the channel to the endpoint driver that is passed to each new connection. - channel: Channel, - // Channel to receive messages from the transport or connections. - rx: mpsc::Receiver, - - // Socket for sending and receiving datagrams. - provider_socket: P, - // Future for writing the next packet to the socket. - next_packet_out: Option, - - // List of all active connections, with a sender to notify them of events. - alive_connections: - HashMap>, - // Channel to forward new inbound connections to the transport. - // `None` if server capabilities are disabled, i.e. the endpoint is only used for dialing. - new_connection_tx: Option>, - // Whether the transport dropped its handle for this endpoint. - is_decoupled: bool, -} - -impl Driver

{ - fn new( - endpoint_config: Arc, - client_config: quinn_proto::ClientConfig, - new_connection_tx: Option>, - server_config: Option>, - channel: Channel, - socket: P, - rx: mpsc::Receiver, - ) -> Self { - Driver { - endpoint: quinn_proto::Endpoint::new(endpoint_config, server_config, false), - client_config, - channel, - rx, - provider_socket: socket, - next_packet_out: None, - alive_connections: HashMap::new(), - new_connection_tx, - is_decoupled: false, - } - } - - /// Handle a message sent from either the [`GenTransport`](super::GenTransport) - /// or a [`crate::Connection`]. - fn handle_message( - &mut self, - to_endpoint: ToEndpoint, - ) -> ControlFlow<(), Option> { - match to_endpoint { - ToEndpoint::Dial { - addr, - result, - version, - } => { - let mut config = self.client_config.clone(); - if version == ProtocolVersion::Draft29 { - config.version(0xff00_001d); - } - // This `"l"` seems necessary because an empty string is an invalid domain - // name. While we don't use domain names, the underlying rustls library - // is based upon the assumption that we do. - let (connection_id, connection) = match self.endpoint.connect(config, addr, "l") { - Ok(c) => c, - Err(err) => { - let _ = result.send(Err(ConnectError::from(err).into())); - return ControlFlow::Continue(None); - } - }; - - debug_assert_eq!(connection.side(), quinn_proto::Side::Client); - let (tx, rx) = mpsc::channel(CHANNEL_CAPACITY); - let connection = Connection::from_quinn_connection( - self.channel.clone(), - connection, - connection_id, - rx, - ); - self.alive_connections.insert(connection_id, tx); - let _ = result.send(Ok(connection)); - } - - // A connection wants to notify the endpoint of something. - ToEndpoint::ProcessConnectionEvent { - connection_id, - event, - } => { - let has_key = self.alive_connections.contains_key(&connection_id); - if !has_key { - return ControlFlow::Continue(None); - } - // We "drained" event indicates that the connection no longer exists and - // its ID can be reclaimed. - let is_drained_event = event.is_drained(); - if is_drained_event { - self.alive_connections.remove(&connection_id); - if self.is_decoupled && self.alive_connections.is_empty() { - log::debug!( - "Driver is decoupled and no active connections remain. Shutting down." - ); - return ControlFlow::Break(()); - } - } - - let event_back = self.endpoint.handle_event(connection_id, event); - - if let Some(event_back) = event_back { - debug_assert!(!is_drained_event); - if let Some(sender) = self.alive_connections.get_mut(&connection_id) { - // We clone the sender to guarantee that there will be at least one - // free slot to send the event. - // The channel can not grow out of bound because an `event_back` is - // only sent if we previously received an event from the same connection. - // If the connection is busy, it won't sent us any more events to handle. - let _ = sender.clone().start_send(event_back); - } else { - log::error!("State mismatch: event for closed connection"); - } - } - } - - // Data needs to be sent on the UDP socket. - ToEndpoint::SendUdpPacket(transmit) => return ControlFlow::Continue(Some(transmit)), - ToEndpoint::Decoupled => self.handle_decoupling()?, - } - ControlFlow::Continue(None) - } - - /// Handle an UDP datagram received on the socket. - /// The datagram content was written into the `socket_recv_buffer`. - fn handle_datagram(&mut self, packet: BytesMut, packet_src: SocketAddr) -> ControlFlow<()> { - let local_ip = self.channel.socket_addr.ip(); - // TODO: ECN bits aren't handled - let (connec_id, event) = - match self - .endpoint - .handle(Instant::now(), packet_src, Some(local_ip), None, packet) - { - Some(event) => event, - None => return ControlFlow::Continue(()), - }; - match event { - quinn_proto::DatagramEvent::ConnectionEvent(event) => { - // `event` has type `quinn_proto::ConnectionEvent`, which has multiple - // variants. `quinn_proto::Endpoint::handle` however only ever returns - // `ConnectionEvent::Datagram`. - debug_assert!(format!("{event:?}").contains("Datagram")); - - // Redirect the datagram to its connection. - if let Some(sender) = self.alive_connections.get_mut(&connec_id) { - match sender.try_send(event) { - Ok(()) => {} - Err(err) if err.is_disconnected() => { - // Connection was dropped by the user. - // Inform the endpoint that this connection is drained. - self.endpoint - .handle_event(connec_id, quinn_proto::EndpointEvent::drained()); - self.alive_connections.remove(&connec_id); - } - Err(err) if err.is_full() => { - // Connection is too busy. Drop the datagram to back-pressure the remote. - log::debug!( - "Dropping packet for connection {:?} because the connection's channel is full.", - connec_id - ); - } - Err(_) => unreachable!("Error is either `Full` or `Disconnected`."), - } - } else { - log::error!("State mismatch: event for closed connection"); - } - } - quinn_proto::DatagramEvent::NewConnection(connec) => { - // A new connection has been received. `connec_id` is a newly-allocated - // identifier. - debug_assert_eq!(connec.side(), quinn_proto::Side::Server); - let connection_tx = match self.new_connection_tx.as_mut() { - Some(tx) => tx, - None => { - debug_assert!(false, "Endpoint reported a new connection even though server capabilities are disabled."); - return ControlFlow::Continue(()); - } - }; - - let (tx, rx) = mpsc::channel(CHANNEL_CAPACITY); - let connection = - Connection::from_quinn_connection(self.channel.clone(), connec, connec_id, rx); - match connection_tx.start_send(connection) { - Ok(()) => { - self.alive_connections.insert(connec_id, tx); - } - Err(e) if e.is_disconnected() => self.handle_decoupling()?, - Err(e) if e.is_full() => log::warn!( - "Dropping new incoming connection {:?} because the channel to the listener is full", - connec_id - ), - Err(_) => unreachable!("Error is either `Full` or `Disconnected`."), - } - } - } - ControlFlow::Continue(()) - } - - /// The transport dropped the channel to this [`Driver`]. - fn handle_decoupling(&mut self) -> ControlFlow<()> { - if self.alive_connections.is_empty() { - return ControlFlow::Break(()); - } - // Listener was closed. - self.endpoint.reject_new_connections(); - self.new_connection_tx = None; - self.is_decoupled = true; - ControlFlow::Continue(()) - } -} - -/// Future that runs until the [`Driver`] is decoupled and not active connections -/// remain -impl Future for Driver

{ - type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - loop { - // Flush any pending pocket so that the socket is reading to write an next - // packet. - match self.provider_socket.poll_send_flush(cx) { - // The pending packet was send or no packet was pending. - Poll::Ready(Ok(_)) => { - // Start sending a packet on the socket. - if let Some(transmit) = self.next_packet_out.take() { - self.provider_socket - .start_send(transmit.contents.into(), transmit.destination); - continue; - } - - // The endpoint might request packets to be sent out. This is handled in - // priority to avoid buffering up packets. - if let Some(transmit) = self.endpoint.poll_transmit() { - self.next_packet_out = Some(transmit); - continue; - } - - // Handle messages from transport and connections. - match self.rx.poll_next_unpin(cx) { - Poll::Ready(Some(to_endpoint)) => match self.handle_message(to_endpoint) { - ControlFlow::Continue(Some(transmit)) => { - self.next_packet_out = Some(transmit); - continue; - } - ControlFlow::Continue(None) => continue, - ControlFlow::Break(()) => break, - }, - Poll::Ready(None) => { - unreachable!("Sender side is never dropped or closed.") - } - Poll::Pending => {} - } - } - // Errors on the socket are expected to never happen, and we handle them by simply - // printing a log message. The packet gets discarded in case of error, but we are - // robust to packet losses and it is consequently not a logic error to proceed with - // normal operations. - Poll::Ready(Err(err)) => { - log::warn!("Error while sending on QUIC UDP socket: {:?}", err); - continue; - } - Poll::Pending => {} - } - - // Poll for new packets from the remote. - match self.provider_socket.poll_recv_from(cx) { - Poll::Ready(Ok((bytes, packet_src))) => { - let bytes_mut = bytes.as_slice().into(); - match self.handle_datagram(bytes_mut, packet_src) { - ControlFlow::Continue(()) => continue, - ControlFlow::Break(()) => break, - } - } - // Errors on the socket are expected to never happen, and we handle them by - // simply printing a log message. - Poll::Ready(Err(err)) => { - log::warn!("Error while receive on QUIC UDP socket: {:?}", err); - continue; - } - Poll::Pending => {} - } - - return Poll::Pending; - } - - Poll::Ready(()) - } -} From e507e441f030c9f9c44170d727097ed6628c92e2 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 24 May 2023 14:37:10 +0000 Subject: [PATCH 30/60] Implement hole punching with SO_REUSEPORT Co-authored-by: Arpan Kapoor --- Cargo.lock | 1 + transports/quic/Cargo.toml | 1 + transports/quic/src/hole_punching.rs | 122 +++++++++++++++++++++++ transports/quic/src/lib.rs | 1 + transports/quic/src/transport.rs | 144 +++++++++++++++++++++------ 5 files changed, 240 insertions(+), 29 deletions(-) create mode 100644 transports/quic/src/hole_punching.rs diff --git a/Cargo.lock b/Cargo.lock index bc6b4d86c91..17e01cd6f25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2911,6 +2911,7 @@ dependencies = [ "quinn", "rand 0.8.5", "rustls 0.21.1", + "socket2 0.5.3", "thiserror", "tokio", ] diff --git a/transports/quic/Cargo.toml b/transports/quic/Cargo.toml index 0853ef646eb..9c8fe539f21 100644 --- a/transports/quic/Cargo.toml +++ b/transports/quic/Cargo.toml @@ -22,6 +22,7 @@ parking_lot = "0.12.0" quinn = { version = "0.10.1", default-features = false, features = ["tls-rustls", "futures-io"] } rand = "0.8.5" rustls = { version = "0.21.1", default-features = false } +socket2 = { version = "0.5.3", features = ["all"] } thiserror = "1.0.40" tokio = { version = "1.28.1", default-features = false, features = ["net", "rt"], optional = true } diff --git a/transports/quic/src/hole_punching.rs b/transports/quic/src/hole_punching.rs new file mode 100644 index 00000000000..c7b36b57d6f --- /dev/null +++ b/transports/quic/src/hole_punching.rs @@ -0,0 +1,122 @@ +use crate::{Connecting, Connection, Error}; + +use libp2p_identity::PeerId; + +use futures::{ + channel::oneshot, + future::{Fuse, FusedFuture}, + prelude::*, +}; +use futures_timer::Delay; + +use rand::{distributions, Rng}; + +use std::{ + collections::HashMap, + io, + net::SocketAddr, + pin::Pin, + sync::{Arc, Mutex}, + task::{Context, Poll}, + time::Duration, +}; + +pub(crate) type HolePunchMap = + Arc>>>; + +pub(crate) struct MaybeHolePunchedConnection { + hole_punch_map: HolePunchMap, + addr: SocketAddr, + upgrade: Fuse, +} + +impl MaybeHolePunchedConnection { + pub(crate) fn new(hole_punch_map: HolePunchMap, addr: SocketAddr, upgrade: Connecting) -> Self { + Self { + hole_punch_map, + addr, + upgrade: upgrade.fuse(), + } + } +} + +impl Future for MaybeHolePunchedConnection { + type Output = Result<(PeerId, Connection), Error>; + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let (peer_id, connection) = futures::ready!(self.upgrade.poll_unpin(cx))?; + let addr = self.addr; + let mut hole_punch_map = self.hole_punch_map.lock().unwrap(); + if let Some(sender) = hole_punch_map.remove(&(addr, peer_id)) { + if let Err(connection) = sender.send((peer_id, connection)) { + Poll::Ready(Ok(connection)) + } else { + Poll::Pending + } + } else { + Poll::Ready(Ok((peer_id, connection))) + } + } +} + +impl FusedFuture for MaybeHolePunchedConnection { + fn is_terminated(&self) -> bool { + self.upgrade.is_terminated() + } +} + +pub(crate) struct HolePuncher { + socket: socket2::Socket, + timeout: Delay, + interval_timeout: Delay, +} + +impl HolePuncher { + pub(crate) fn new( + local_addr: SocketAddr, + remote_addr: SocketAddr, + timeout: Duration, + ) -> io::Result { + let domain = socket2::Domain::for_address(remote_addr); + let socket = socket2::Socket::new(domain, socket2::Type::DGRAM, None)?; + socket.set_reuse_port(true)?; + socket.bind(&local_addr.into())?; + socket.connect(&remote_addr.into())?; + + Ok(Self { + socket, + timeout: Delay::new(timeout), + interval_timeout: Delay::new(Duration::from_secs(0)), + }) + } +} + +/// Never finishes successfully, only with an Err (timeout) +impl Future for HolePuncher { + type Output = Error; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.timeout.poll_unpin(cx) { + Poll::Ready(_) => return Poll::Ready(Error::HandshakeTimedOut), + Poll::Pending => {} + } + + let _ = futures::ready!(self.interval_timeout.poll_unpin(cx)); + + let contents: Vec = rand::thread_rng() + .sample_iter(distributions::Standard) + .take(64) + .collect(); + + if let Err(e) = self.socket.send(&contents) { + if !matches!(e.kind(), io::ErrorKind::WouldBlock) { + return Poll::Ready(Error::Io(e)); + } + } + + self.interval_timeout.reset(Duration::from_millis( + rand::thread_rng().gen_range(10..=200), + )); + + Poll::Pending + } +} diff --git a/transports/quic/src/lib.rs b/transports/quic/src/lib.rs index 42e6360bff9..2779c625c36 100644 --- a/transports/quic/src/lib.rs +++ b/transports/quic/src/lib.rs @@ -59,6 +59,7 @@ mod config; mod connection; +mod hole_punching; mod provider; mod transport; diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index a86bcbbf079..1b4f2f97156 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -19,9 +19,11 @@ // DEALINGS IN THE SOFTWARE. use crate::config::{Config, QuinnConfig}; +use crate::hole_punching::*; use crate::provider::Provider; use crate::{ConnectError, Connecting, Connection, Error}; +use futures::channel::oneshot; use futures::future::BoxFuture; use futures::ready; use futures::stream::StreamExt; @@ -40,7 +42,6 @@ use std::collections::HashMap; use std::fmt; use std::hash::{Hash, Hasher}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use std::sync::Arc; use std::time::Duration; use std::{ net::SocketAddr, @@ -69,6 +70,8 @@ pub struct GenTransport { support_draft_29: bool, /// Streams of active [`Listener`]s. listeners: SelectAll>, + /// Hole punching attempts + hole_punch_map: HolePunchMap, /// Dialer for each socket family if no matching listener exists. dialer: HashMap, /// Waker to poll the transport again when a new dialer or listener is added. @@ -83,6 +86,7 @@ impl GenTransport

{ let quinn_config = config.into(); Self { listeners: SelectAll::new(), + hole_punch_map: Default::default(), quinn_config, handshake_timeout, dialer: HashMap::new(), @@ -100,16 +104,24 @@ impl GenTransport

{ match P::runtime() { #[cfg(feature = "tokio")] Runtime::Tokio => { - let runtime = Arc::new(quinn::TokioRuntime); - let socket = std::net::UdpSocket::bind(socket_addr)?; + let runtime = std::sync::Arc::new(quinn::TokioRuntime); + let domain = socket2::Domain::for_address(socket_addr); + let socket = socket2::Socket::new(domain, socket2::Type::DGRAM, None)?; + socket.set_reuse_port(true)?; + socket.bind(&socket_addr.into())?; + let socket = socket.into(); let endpoint = quinn::Endpoint::new(endpoint_config, server_config, socket, runtime)?; Ok(endpoint) } #[cfg(feature = "async-std")] Runtime::AsyncStd => { - let runtime = Arc::new(quinn::AsyncStdRuntime); - let socket = std::net::UdpSocket::bind(socket_addr)?; + let runtime = std::sync::Arc::new(quinn::AsyncStdRuntime); + let domain = socket2::Domain::for_address(socket_addr); + let socket = socket2::Socket::new(domain, socket2::Type::DGRAM, None)?; + socket.set_reuse_port(true)?; + socket.bind(&socket_addr.into())?; + let socket = socket.into(); let endpoint = quinn::Endpoint::new(endpoint_config, server_config, socket, runtime)?; Ok(endpoint) @@ -128,7 +140,7 @@ impl GenTransport

{ impl Transport for GenTransport

{ type Output = (PeerId, Connection); type Error = Error; - type ListenerUpgrade = Connecting; + type ListenerUpgrade = BoxFuture<'static, Result>; type Dial = BoxFuture<'static, Result>; fn listen_on( @@ -136,8 +148,9 @@ impl Transport for GenTransport

{ listener_id: ListenerId, addr: Multiaddr, ) -> Result<(), TransportError> { - let (socket_addr, version) = multiaddr_to_socketaddr(&addr, self.support_draft_29) - .ok_or(TransportError::MultiaddrNotSupported(addr))?; + let (socket_addr, version, _peer_id) = + multiaddr_to_socketaddr(&addr, self.support_draft_29) + .ok_or(TransportError::MultiaddrNotSupported(addr))?; let endpoint_config = self.quinn_config.endpoint_config.clone(); let server_config = self.quinn_config.server_config.clone(); let endpoint = Self::new_endpoint(endpoint_config, Some(server_config), socket_addr)?; @@ -145,6 +158,7 @@ impl Transport for GenTransport

{ listener_id, socket_addr, endpoint, + self.hole_punch_map.clone(), self.handshake_timeout, version, )?; @@ -183,8 +197,9 @@ impl Transport for GenTransport

{ } fn dial(&mut self, addr: Multiaddr) -> Result> { - let (socket_addr, version) = multiaddr_to_socketaddr(&addr, self.support_draft_29) - .ok_or_else(|| TransportError::MultiaddrNotSupported(addr.clone()))?; + let (socket_addr, version, _peer_id) = + multiaddr_to_socketaddr(&addr, self.support_draft_29) + .ok_or_else(|| TransportError::MultiaddrNotSupported(addr.clone()))?; if socket_addr.port() == 0 || socket_addr.ip().is_unspecified() { return Err(TransportError::MultiaddrNotSupported(addr)); } @@ -255,11 +270,60 @@ impl Transport for GenTransport

{ &mut self, addr: Multiaddr, ) -> Result> { - // TODO: As the listener of a QUIC hole punch, we need to send a random UDP packet to the - // `addr`. See DCUtR specification below. - // - // https://github.com/libp2p/specs/blob/master/relay/DCUtR.md#the-protocol - Err(TransportError::MultiaddrNotSupported(addr)) + let (socket_addr, _version, peer_id) = + multiaddr_to_socketaddr(&addr, self.support_draft_29) + .ok_or_else(|| TransportError::MultiaddrNotSupported(addr.clone()))?; + if socket_addr.port() == 0 || socket_addr.ip().is_unspecified() { + return Err(TransportError::MultiaddrNotSupported(addr)); + } + let Some(peer_id) = peer_id else { + return Err(TransportError::MultiaddrNotSupported(addr)); + }; + + let listeners = self + .listeners + .iter_mut() + .filter(|l| { + if l.is_closed { + return false; + } + let listen_addr = l.socket_addr(); + SocketFamily::is_same(&listen_addr.ip(), &socket_addr.ip()) + && listen_addr.ip().is_loopback() == socket_addr.ip().is_loopback() + }) + .collect::>(); + + let endpoint_addr = match listeners.len() { + 0 => { + return Err(TransportError::MultiaddrNotSupported(addr)); // FIXME return correct error + } + _ => { + // Pick any listener to use for dialing. + // We hash the socket address to achieve determinism. + let mut hasher = DefaultHasher::new(); + socket_addr.hash(&mut hasher); + let index = hasher.finish() as usize % listeners.len(); + listeners[index].endpoint.local_addr().unwrap() + } + }; + + let hole_puncher = HolePuncher::new(endpoint_addr, socket_addr, self.handshake_timeout) + .map_err(|e| TransportError::Other(Error::Io(e)))?; + + let (sender, receiver) = oneshot::channel(); + let mut hole_punch_map = self.hole_punch_map.lock().unwrap(); + hole_punch_map.insert((socket_addr, peer_id), sender); + + Ok(Box::pin(async { + futures::select! { + hole_punched = receiver.fuse() => { + Ok(hole_punched.unwrap()) + }, + err = hole_puncher.fuse() => { + Err(err) + } + } + })) } fn poll( @@ -302,6 +366,9 @@ struct Listener { /// None if we are only listening on a single interface. if_watcher: Option, + /// Hole punching attempts + hole_punch_map: HolePunchMap, + /// Whether the listener was closed and the stream should terminate. is_closed: bool, @@ -317,6 +384,7 @@ impl Listener

{ listener_id: ListenerId, socket_addr: SocketAddr, endpoint: quinn::Endpoint, + hole_punch_map: HolePunchMap, handshake_timeout: Duration, version: ProtocolVersion, ) -> Result { @@ -339,6 +407,7 @@ impl Listener

{ Ok(Listener { endpoint, + hole_punch_map, accept, listener_id, version, @@ -416,7 +485,7 @@ impl Listener

{ } impl Stream for Listener

{ - type Item = TransportEvent; + type Item = TransportEvent< as Transport>::ListenerUpgrade, Error>; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { loop { if let Some(event) = self.pending_event.take() { @@ -435,10 +504,18 @@ impl Stream for Listener

{ self.accept = async move { endpoint.accept().await }.boxed(); let local_addr = socketaddr_to_multiaddr(&self.socket_addr(), self.version); - let send_back_addr = - socketaddr_to_multiaddr(&connecting.remote_address(), self.version); + let remote_addr = connecting.remote_address(); + let send_back_addr = socketaddr_to_multiaddr(&remote_addr, self.version); + + let upgrade = MaybeHolePunchedConnection::new( + self.hole_punch_map.clone(), + remote_addr, + Connecting::new(connecting, self.handshake_timeout), + ) + .boxed(); + let event = TransportEvent::Incoming { - upgrade: Connecting::new(connecting, self.handshake_timeout), + upgrade, local_addr, send_back_addr, listener_id: self.listener_id, @@ -526,15 +603,19 @@ fn ip_to_listenaddr( fn multiaddr_to_socketaddr( addr: &Multiaddr, support_draft_29: bool, -) -> Option<(SocketAddr, ProtocolVersion)> { +) -> Option<(SocketAddr, ProtocolVersion, Option)> { let mut iter = addr.iter(); let proto1 = iter.next()?; let proto2 = iter.next()?; let proto3 = iter.next()?; + let mut peer_id = None; for proto in iter { match proto { - Protocol::P2p(_) => {} // Ignore a `/p2p/...` prefix of possibly outer protocols, if present. + Protocol::P2p(id) => { + let new_peer_id = PeerId::from_multihash(id).ok()?; + peer_id.replace(new_peer_id); + } _ => return None, } } @@ -546,10 +627,10 @@ fn multiaddr_to_socketaddr( match (proto1, proto2) { (Protocol::Ip4(ip), Protocol::Udp(port)) => { - Some((SocketAddr::new(ip.into(), port), version)) + Some((SocketAddr::new(ip.into(), port), version, peer_id)) } (Protocol::Ip6(ip), Protocol::Udp(port)) => { - Some((SocketAddr::new(ip.into(), port), version)) + Some((SocketAddr::new(ip.into(), port), version, peer_id)) } _ => None, } @@ -622,7 +703,8 @@ mod test { ), Some(( SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 12345,), - ProtocolVersion::V1 + ProtocolVersion::V1, + None )) ); assert_eq!( @@ -634,7 +716,8 @@ mod test { ), Some(( SocketAddr::new(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), 8080,), - ProtocolVersion::V1 + ProtocolVersion::V1, + None )) ); assert_eq!( @@ -646,7 +729,7 @@ mod test { Some((SocketAddr::new( IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 55148, - ), ProtocolVersion::V1)) + ), ProtocolVersion::V1, Some("12D3KooW9xk7Zp1gejwfwNpfm6L9zH5NL4Bx5rm94LRYJJHJuARZ".parse().unwrap()))) ); assert_eq!( multiaddr_to_socketaddr( @@ -655,7 +738,8 @@ mod test { ), Some(( SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 12345,), - ProtocolVersion::V1 + ProtocolVersion::V1, + None )) ); assert_eq!( @@ -672,7 +756,8 @@ mod test { )), 8080, ), - ProtocolVersion::V1 + ProtocolVersion::V1, + None )) ); @@ -688,7 +773,8 @@ mod test { ), Some(( SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 1234,), - ProtocolVersion::Draft29 + ProtocolVersion::Draft29, + None )) ); } From c48b8e60865e393c4c72d55deb63aadc539da3f0 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 24 May 2023 14:51:55 +0000 Subject: [PATCH 31/60] fix clippy --- transports/quic/src/hole_punching.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transports/quic/src/hole_punching.rs b/transports/quic/src/hole_punching.rs index c7b36b57d6f..e25248769ab 100644 --- a/transports/quic/src/hole_punching.rs +++ b/transports/quic/src/hole_punching.rs @@ -100,7 +100,7 @@ impl Future for HolePuncher { Poll::Pending => {} } - let _ = futures::ready!(self.interval_timeout.poll_unpin(cx)); + futures::ready!(self.interval_timeout.poll_unpin(cx)); let contents: Vec = rand::thread_rng() .sample_iter(distributions::Standard) From af684cedb583f76c1febdef4dac75bda27451dee Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 7 Jun 2023 06:15:08 +0000 Subject: [PATCH 32/60] Use try_clone UdpSocket instead of PORT_REUSE --- Cargo.lock | 1 - transports/quic/Cargo.toml | 1 - transports/quic/src/hole_punching.rs | 16 +++----- transports/quic/src/transport.rs | 55 ++++++++++++++++------------ 4 files changed, 37 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 17e01cd6f25..bc6b4d86c91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2911,7 +2911,6 @@ dependencies = [ "quinn", "rand 0.8.5", "rustls 0.21.1", - "socket2 0.5.3", "thiserror", "tokio", ] diff --git a/transports/quic/Cargo.toml b/transports/quic/Cargo.toml index 9c8fe539f21..0853ef646eb 100644 --- a/transports/quic/Cargo.toml +++ b/transports/quic/Cargo.toml @@ -22,7 +22,6 @@ parking_lot = "0.12.0" quinn = { version = "0.10.1", default-features = false, features = ["tls-rustls", "futures-io"] } rand = "0.8.5" rustls = { version = "0.21.1", default-features = false } -socket2 = { version = "0.5.3", features = ["all"] } thiserror = "1.0.40" tokio = { version = "1.28.1", default-features = false, features = ["net", "rt"], optional = true } diff --git a/transports/quic/src/hole_punching.rs b/transports/quic/src/hole_punching.rs index e25248769ab..f1099fb63be 100644 --- a/transports/quic/src/hole_punching.rs +++ b/transports/quic/src/hole_punching.rs @@ -14,7 +14,7 @@ use rand::{distributions, Rng}; use std::{ collections::HashMap, io, - net::SocketAddr, + net::{SocketAddr, UdpSocket}, pin::Pin, sync::{Arc, Mutex}, task::{Context, Poll}, @@ -65,25 +65,21 @@ impl FusedFuture for MaybeHolePunchedConnection { } pub(crate) struct HolePuncher { - socket: socket2::Socket, + socket: UdpSocket, + remote_addr: SocketAddr, timeout: Delay, interval_timeout: Delay, } impl HolePuncher { pub(crate) fn new( - local_addr: SocketAddr, + socket: UdpSocket, remote_addr: SocketAddr, timeout: Duration, ) -> io::Result { - let domain = socket2::Domain::for_address(remote_addr); - let socket = socket2::Socket::new(domain, socket2::Type::DGRAM, None)?; - socket.set_reuse_port(true)?; - socket.bind(&local_addr.into())?; - socket.connect(&remote_addr.into())?; - Ok(Self { socket, + remote_addr, timeout: Delay::new(timeout), interval_timeout: Delay::new(Duration::from_secs(0)), }) @@ -107,7 +103,7 @@ impl Future for HolePuncher { .take(64) .collect(); - if let Err(e) = self.socket.send(&contents) { + if let Err(e) = self.socket.send_to(&contents, self.remote_addr) { if !matches!(e.kind(), io::ErrorKind::WouldBlock) { return Poll::Ready(Error::Io(e)); } diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index 1b4f2f97156..75fcb784311 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -41,7 +41,7 @@ use std::collections::hash_map::{DefaultHasher, Entry}; use std::collections::HashMap; use std::fmt; use std::hash::{Hash, Hasher}; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, UdpSocket}; use std::time::Duration; use std::{ net::SocketAddr, @@ -98,18 +98,13 @@ impl GenTransport

{ fn new_endpoint( endpoint_config: quinn::EndpointConfig, server_config: Option, - socket_addr: SocketAddr, + socket: UdpSocket, ) -> Result { use crate::provider::Runtime; match P::runtime() { #[cfg(feature = "tokio")] Runtime::Tokio => { let runtime = std::sync::Arc::new(quinn::TokioRuntime); - let domain = socket2::Domain::for_address(socket_addr); - let socket = socket2::Socket::new(domain, socket2::Type::DGRAM, None)?; - socket.set_reuse_port(true)?; - socket.bind(&socket_addr.into())?; - let socket = socket.into(); let endpoint = quinn::Endpoint::new(endpoint_config, server_config, socket, runtime)?; Ok(endpoint) @@ -117,11 +112,6 @@ impl GenTransport

{ #[cfg(feature = "async-std")] Runtime::AsyncStd => { let runtime = std::sync::Arc::new(quinn::AsyncStdRuntime); - let domain = socket2::Domain::for_address(socket_addr); - let socket = socket2::Socket::new(domain, socket2::Type::DGRAM, None)?; - socket.set_reuse_port(true)?; - socket.bind(&socket_addr.into())?; - let socket = socket.into(); let endpoint = quinn::Endpoint::new(endpoint_config, server_config, socket, runtime)?; Ok(endpoint) @@ -129,7 +119,7 @@ impl GenTransport

{ Runtime::Dummy => { let _ = endpoint_config; let _ = server_config; - let _ = socket_addr; + let _ = socket; let err = std::io::Error::new(std::io::ErrorKind::Other, "no async runtime found"); Err(Error::Io(err)) } @@ -153,13 +143,17 @@ impl Transport for GenTransport

{ .ok_or(TransportError::MultiaddrNotSupported(addr))?; let endpoint_config = self.quinn_config.endpoint_config.clone(); let server_config = self.quinn_config.server_config.clone(); - let endpoint = Self::new_endpoint(endpoint_config, Some(server_config), socket_addr)?; + let need_if_watcher = socket_addr.ip().is_unspecified(); + let socket = UdpSocket::bind(socket_addr).map_err(Self::Error::from)?; + let socket_c = socket.try_clone().map_err(Self::Error::from)?; + let endpoint = Self::new_endpoint(endpoint_config, Some(server_config), socket)?; let listener = Listener::new( listener_id, - socket_addr, + socket_c, endpoint, self.hole_punch_map.clone(), self.handshake_timeout, + need_if_watcher, version, )?; self.listeners.push(listener); @@ -231,9 +225,10 @@ impl Transport for GenTransport

{ SocketFamily::Ipv4 => SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), SocketFamily::Ipv6 => SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0), }; + let socket = + UdpSocket::bind(listen_socket_addr).map_err(Self::Error::from)?; let endpoint_config = self.quinn_config.endpoint_config.clone(); - let endpoint = - Self::new_endpoint(endpoint_config, None, listen_socket_addr)?; + let endpoint = Self::new_endpoint(endpoint_config, None, socket)?; vacant.insert(endpoint.clone()); endpoint @@ -293,7 +288,7 @@ impl Transport for GenTransport

{ }) .collect::>(); - let endpoint_addr = match listeners.len() { + let socket = match listeners.len() { 0 => { return Err(TransportError::MultiaddrNotSupported(addr)); // FIXME return correct error } @@ -303,11 +298,13 @@ impl Transport for GenTransport

{ let mut hasher = DefaultHasher::new(); socket_addr.hash(&mut hasher); let index = hasher.finish() as usize % listeners.len(); - listeners[index].endpoint.local_addr().unwrap() + listeners[index] + .try_clone_socket() + .map_err(Self::Error::from)? } }; - let hole_puncher = HolePuncher::new(endpoint_addr, socket_addr, self.handshake_timeout) + let hole_puncher = HolePuncher::new(socket, socket_addr, self.handshake_timeout) .map_err(|e| TransportError::Other(Error::Io(e)))?; let (sender, receiver) = oneshot::channel(); @@ -356,6 +353,9 @@ struct Listener { /// Endpoint endpoint: quinn::Endpoint, + /// An underlying copy of the socket to be able to hole punch with + socket: UdpSocket, + /// A future to poll new incoming connections. accept: BoxFuture<'static, Option>, /// Timeout for connection establishment on inbound connections. @@ -382,20 +382,21 @@ struct Listener { impl Listener

{ fn new( listener_id: ListenerId, - socket_addr: SocketAddr, + socket: UdpSocket, endpoint: quinn::Endpoint, hole_punch_map: HolePunchMap, handshake_timeout: Duration, + need_if_watcher: bool, version: ProtocolVersion, ) -> Result { let if_watcher; let pending_event; - if socket_addr.ip().is_unspecified() { + if need_if_watcher { if_watcher = Some(P::new_if_watcher()?); pending_event = None; } else { if_watcher = None; - let ma = socketaddr_to_multiaddr(&endpoint.local_addr()?, version); + let ma = socketaddr_to_multiaddr(&socket.local_addr()?, version); pending_event = Some(TransportEvent::NewAddress { listener_id, listen_addr: ma, @@ -408,6 +409,7 @@ impl Listener

{ Ok(Listener { endpoint, hole_punch_map, + socket, accept, listener_id, version, @@ -438,8 +440,13 @@ impl Listener

{ } } + /// Clone underlying socket (for hole punching). + fn try_clone_socket(&self) -> std::io::Result { + self.socket.try_clone() + } + fn socket_addr(&self) -> SocketAddr { - self.endpoint.local_addr().unwrap() + self.socket.local_addr().unwrap() } /// Poll for a next If Event. From 608e94d71bb6774ef9fb226aca8ff0d8486f0419 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 7 Jun 2023 09:31:39 +0300 Subject: [PATCH 33/60] Update transports/quic/src/config.rs Co-authored-by: Max Inden --- transports/quic/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transports/quic/src/config.rs b/transports/quic/src/config.rs index 2f235938da8..1033225e26c 100644 --- a/transports/quic/src/config.rs +++ b/transports/quic/src/config.rs @@ -25,7 +25,7 @@ use std::{sync::Arc, time::Duration}; #[derive(Clone)] pub struct Config { /// Timeout for the initial handshake when establishing a connection. - /// The actual timeout is the minimum of this an the [`Config::max_idle_timeout`]. + /// The actual timeout is the minimum of this and the [`Config::max_idle_timeout`]. pub handshake_timeout: Duration, /// Maximum duration of inactivity in ms to accept before timing out the connection. pub max_idle_timeout: u32, From 78f3e3c57a9b75d1dcd0ae082dd5838754221566 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 7 Jun 2023 06:47:39 +0000 Subject: [PATCH 34/60] Explain why Listener::socket_addr cannot fail --- transports/quic/src/transport.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index 75fcb784311..b7d411d390d 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -446,7 +446,9 @@ impl Listener

{ } fn socket_addr(&self) -> SocketAddr { - self.socket.local_addr().unwrap() + self.socket + .local_addr() + .expect("Cannot fail because the socket is bound") } /// Poll for a next If Event. From be97ffe2e8579a20596a66176aa9cdad9ea33d24 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 7 Jun 2023 06:49:55 +0000 Subject: [PATCH 35/60] Simplify calls to .poll/.poll_unpin --- transports/quic/src/connection/connecting.rs | 8 +++----- transports/quic/src/connection/substream.rs | 21 +++++++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/transports/quic/src/connection/connecting.rs b/transports/quic/src/connection/connecting.rs index bb750736a7a..b911eaa7dfe 100644 --- a/transports/quic/src/connection/connecting.rs +++ b/transports/quic/src/connection/connecting.rs @@ -23,7 +23,7 @@ use crate::{Connection, ConnectionError, Error}; use futures::{ - future::{select, Either, Select}, + future::{select, Either, FutureExt, Select}, prelude::*, }; use futures_timer::Delay; @@ -69,10 +69,8 @@ impl Connecting { impl Future for Connecting { type Output = Result<(PeerId, Connection), Error>; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let connecting = Pin::new(&mut self.get_mut().connecting); - - let connection = match futures::ready!(connecting.poll(cx)) { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let connection = match futures::ready!(self.connecting.poll_unpin(cx)) { Either::Right(_) => return Poll::Ready(Err(Error::HandshakeTimedOut)), Either::Left((connection, _)) => connection.map_err(ConnectionError)?, }; diff --git a/transports/quic/src/connection/substream.rs b/transports/quic/src/connection/substream.rs index 80438bf0c65..02dceecc259 100644 --- a/transports/quic/src/connection/substream.rs +++ b/transports/quic/src/connection/substream.rs @@ -42,25 +42,28 @@ impl Substream { impl AsyncRead for Substream { fn poll_read( - self: Pin<&mut Self>, + mut self: Pin<&mut Self>, cx: &mut Context, buf: &mut [u8], ) -> Poll> { - AsyncRead::poll_read(Pin::new(&mut self.get_mut().recv), cx, buf) + Pin::new(&mut self.recv).poll_read(cx, buf) } } impl AsyncWrite for Substream { - fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll> { - AsyncWrite::poll_write(Pin::new(&mut self.get_mut().send), cx, buf) + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut self.send).poll_write(cx, buf) } - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - AsyncWrite::poll_flush(Pin::new(&mut self.get_mut().send), cx) + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + Pin::new(&mut self.send).poll_flush(cx) } - fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let this = self.get_mut(); - AsyncWrite::poll_close(Pin::new(&mut this.send), cx) + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + Pin::new(&mut self.send).poll_close(cx) } } From d4de2d59b1c4dac869e75012e8199153e99fff12 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 7 Jun 2023 06:59:19 +0000 Subject: [PATCH 36/60] Rename Substream -> Stream --- transports/quic/src/connection.rs | 14 +++++++------- .../src/connection/{substream.rs => stream.rs} | 8 ++++---- transports/quic/src/lib.rs | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) rename transports/quic/src/connection/{substream.rs => stream.rs} (95%) diff --git a/transports/quic/src/connection.rs b/transports/quic/src/connection.rs index 4bda971a592..70cc1a9c518 100644 --- a/transports/quic/src/connection.rs +++ b/transports/quic/src/connection.rs @@ -19,10 +19,10 @@ // DEALINGS IN THE SOFTWARE. mod connecting; -mod substream; +mod stream; pub use connecting::Connecting; -pub use substream::Substream; +pub use stream::Stream; use crate::{ConnectionError, Error}; @@ -62,7 +62,7 @@ impl Connection { } impl StreamMuxer for Connection { - type Substream = Substream; + type Substream = Stream; type Error = Error; fn poll_inbound( @@ -78,8 +78,8 @@ impl StreamMuxer for Connection { let (send, recv) = futures::ready!(incoming.poll_unpin(cx)).map_err(ConnectionError)?; this.incoming.take(); - let substream = Substream::new(send, recv); - Poll::Ready(Ok(substream)) + let stream = Stream::new(send, recv); + Poll::Ready(Ok(stream)) } fn poll_outbound( @@ -95,8 +95,8 @@ impl StreamMuxer for Connection { let (send, recv) = futures::ready!(outgoing.poll_unpin(cx)).map_err(ConnectionError)?; this.outgoing.take(); - let substream = Substream::new(send, recv); - Poll::Ready(Ok(substream)) + let stream = Stream::new(send, recv); + Poll::Ready(Ok(stream)) } fn poll( diff --git a/transports/quic/src/connection/substream.rs b/transports/quic/src/connection/stream.rs similarity index 95% rename from transports/quic/src/connection/substream.rs rename to transports/quic/src/connection/stream.rs index 02dceecc259..a93ed555a67 100644 --- a/transports/quic/src/connection/substream.rs +++ b/transports/quic/src/connection/stream.rs @@ -27,20 +27,20 @@ use std::{ use futures::{AsyncRead, AsyncWrite}; /// A single stream on a connection -pub struct Substream { +pub struct Stream { /// A send part of the stream send: quinn::SendStream, /// A receive part of the stream recv: quinn::RecvStream, } -impl Substream { +impl Stream { pub(super) fn new(send: quinn::SendStream, recv: quinn::RecvStream) -> Self { Self { send, recv } } } -impl AsyncRead for Substream { +impl AsyncRead for Stream { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context, @@ -50,7 +50,7 @@ impl AsyncRead for Substream { } } -impl AsyncWrite for Substream { +impl AsyncWrite for Stream { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context, diff --git a/transports/quic/src/lib.rs b/transports/quic/src/lib.rs index 2779c625c36..e098819f9ed 100644 --- a/transports/quic/src/lib.rs +++ b/transports/quic/src/lib.rs @@ -64,7 +64,7 @@ mod provider; mod transport; pub use config::Config; -pub use connection::{Connecting, Connection, Substream}; +pub use connection::{Connecting, Connection, Stream}; #[cfg(feature = "async-std")] pub use provider::async_std; #[cfg(feature = "tokio")] From 686ec3f3a2da0eb9d7152adc503613f6e5080c88 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 7 Jun 2023 07:46:28 +0000 Subject: [PATCH 37/60] Fix merge --- transports/quic/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transports/quic/src/config.rs b/transports/quic/src/config.rs index 1033225e26c..201594e247c 100644 --- a/transports/quic/src/config.rs +++ b/transports/quic/src/config.rs @@ -65,7 +65,7 @@ pub struct Config { impl Config { /// Creates a new configuration object with default values. - pub fn new(keypair: &libp2p_core::identity::Keypair) -> Self { + pub fn new(keypair: &libp2p_identity::Keypair) -> Self { let client_tls_config = Arc::new(libp2p_tls::make_client_config(keypair, None).unwrap()); let server_tls_config = Arc::new(libp2p_tls::make_server_config(keypair).unwrap()); Self { From 0b4901068312f053826bd9bfd577428841e8a230 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 7 Jun 2023 08:02:37 +0000 Subject: [PATCH 38/60] Remove duplicate code --- transports/quic/src/transport.rs | 80 ++++++++++++++++---------------- 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index b7d411d390d..323feae4b8f 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -125,6 +125,39 @@ impl GenTransport

{ } } } + + /// Extract the addr, quic version and peer id from the given [`Multiaddr`]. + fn remote_multiaddr_to_socketaddr( + &self, + addr: Multiaddr, + check_unspecified_addr: bool, + ) -> Result< + (SocketAddr, ProtocolVersion, Option), + TransportError<::Error>, + > { + let (socket_addr, version, peer_id) = multiaddr_to_socketaddr(&addr, self.support_draft_29) + .ok_or_else(|| TransportError::MultiaddrNotSupported(addr.clone()))?; + if check_unspecified_addr { + if socket_addr.port() == 0 || socket_addr.ip().is_unspecified() { + return Err(TransportError::MultiaddrNotSupported(addr)); + } + } + Ok((socket_addr, version, peer_id)) + } + + fn eligible_listeners(&mut self, socket_addr: &SocketAddr) -> Vec<&mut Listener

> { + self.listeners + .iter_mut() + .filter(|l| { + if l.is_closed { + return false; + } + let listen_addr = l.socket_addr(); + SocketFamily::is_same(&listen_addr.ip(), &socket_addr.ip()) + && listen_addr.ip().is_loopback() == socket_addr.ip().is_loopback() + }) + .collect() + } } impl Transport for GenTransport

{ @@ -138,9 +171,7 @@ impl Transport for GenTransport

{ listener_id: ListenerId, addr: Multiaddr, ) -> Result<(), TransportError> { - let (socket_addr, version, _peer_id) = - multiaddr_to_socketaddr(&addr, self.support_draft_29) - .ok_or(TransportError::MultiaddrNotSupported(addr))?; + let (socket_addr, version, _peer_id) = self.remote_multiaddr_to_socketaddr(addr, false)?; let endpoint_config = self.quinn_config.endpoint_config.clone(); let server_config = self.quinn_config.server_config.clone(); let need_if_watcher = socket_addr.ip().is_unspecified(); @@ -191,25 +222,9 @@ impl Transport for GenTransport

{ } fn dial(&mut self, addr: Multiaddr) -> Result> { - let (socket_addr, version, _peer_id) = - multiaddr_to_socketaddr(&addr, self.support_draft_29) - .ok_or_else(|| TransportError::MultiaddrNotSupported(addr.clone()))?; - if socket_addr.port() == 0 || socket_addr.ip().is_unspecified() { - return Err(TransportError::MultiaddrNotSupported(addr)); - } + let (socket_addr, version, _peer_id) = self.remote_multiaddr_to_socketaddr(addr, true)?; - let listeners = self - .listeners - .iter_mut() - .filter(|l| { - if l.is_closed { - return false; - } - let listen_addr = l.socket_addr(); - SocketFamily::is_same(&listen_addr.ip(), &socket_addr.ip()) - && listen_addr.ip().is_loopback() == socket_addr.ip().is_loopback() - }) - .collect::>(); + let listeners = self.eligible_listeners(&socket_addr); let endpoint = match listeners.len() { 0 => { @@ -266,27 +281,10 @@ impl Transport for GenTransport

{ addr: Multiaddr, ) -> Result> { let (socket_addr, _version, peer_id) = - multiaddr_to_socketaddr(&addr, self.support_draft_29) - .ok_or_else(|| TransportError::MultiaddrNotSupported(addr.clone()))?; - if socket_addr.port() == 0 || socket_addr.ip().is_unspecified() { - return Err(TransportError::MultiaddrNotSupported(addr)); - } - let Some(peer_id) = peer_id else { - return Err(TransportError::MultiaddrNotSupported(addr)); - }; + self.remote_multiaddr_to_socketaddr(addr.clone(), true)?; + let peer_id = peer_id.ok_or(TransportError::MultiaddrNotSupported(addr.clone()))?; - let listeners = self - .listeners - .iter_mut() - .filter(|l| { - if l.is_closed { - return false; - } - let listen_addr = l.socket_addr(); - SocketFamily::is_same(&listen_addr.ip(), &socket_addr.ip()) - && listen_addr.ip().is_loopback() == socket_addr.ip().is_loopback() - }) - .collect::>(); + let listeners = self.eligible_listeners(&socket_addr); let socket = match listeners.len() { 0 => { From dcd1eda672910ab916ace61967e328bc15dd3df5 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 13 Jun 2023 14:52:06 +0000 Subject: [PATCH 39/60] Fix clippy --- transports/quic/src/transport.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index 12778f317d4..baf11be5e08 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -138,10 +138,8 @@ impl GenTransport

{ > { let (socket_addr, version, peer_id) = multiaddr_to_socketaddr(&addr, self.support_draft_29) .ok_or_else(|| TransportError::MultiaddrNotSupported(addr.clone()))?; - if check_unspecified_addr { - if socket_addr.port() == 0 || socket_addr.ip().is_unspecified() { - return Err(TransportError::MultiaddrNotSupported(addr)); - } + if check_unspecified_addr && (socket_addr.port() == 0 || socket_addr.ip().is_unspecified()) { + return Err(TransportError::MultiaddrNotSupported(addr)); } Ok((socket_addr, version, peer_id)) } @@ -285,7 +283,7 @@ impl Transport for GenTransport

{ ) -> Result> { let (socket_addr, _version, peer_id) = self.remote_multiaddr_to_socketaddr(addr.clone(), true)?; - let peer_id = peer_id.ok_or(TransportError::MultiaddrNotSupported(addr.clone()))?; + let peer_id = peer_id.ok_or(TransportError::MultiaddrNotSupported(addr))?; let socket = self .eligible_listener(&socket_addr) From 49a9e27c5f1ec2e2a4501c87313e45d916f5d16a Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 13 Jun 2023 14:55:46 +0000 Subject: [PATCH 40/60] Fix fmt --- transports/quic/src/transport.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index baf11be5e08..e524e726474 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -138,7 +138,8 @@ impl GenTransport

{ > { let (socket_addr, version, peer_id) = multiaddr_to_socketaddr(&addr, self.support_draft_29) .ok_or_else(|| TransportError::MultiaddrNotSupported(addr.clone()))?; - if check_unspecified_addr && (socket_addr.port() == 0 || socket_addr.ip().is_unspecified()) { + if check_unspecified_addr && (socket_addr.port() == 0 || socket_addr.ip().is_unspecified()) + { return Err(TransportError::MultiaddrNotSupported(addr)); } Ok((socket_addr, version, peer_id)) From e90556d7a0592b441716e34d9a0358be58e38f4d Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 13 Jun 2023 15:05:48 +0000 Subject: [PATCH 41/60] Revert changes in eligible_listener to match master --- transports/quic/src/transport.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index e524e726474..1a3c39fd5ba 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -159,15 +159,17 @@ impl GenTransport

{ && listen_addr.ip().is_loopback() == socket_addr.ip().is_loopback() }) .collect(); - if listeners.is_empty() { - None - } else { - // Pick any listener to use for dialing. - // We hash the socket address to achieve determinism. - let mut hasher = DefaultHasher::new(); - socket_addr.hash(&mut hasher); - let index = hasher.finish() as usize % listeners.len(); - Some(listeners.swap_remove(index)) + match listeners.len() { + 0 => None, + 1 => listeners.pop(), + _ => { + // Pick any listener to use for dialing. + // We hash the socket address to achieve determinism. + let mut hasher = DefaultHasher::new(); + socket_addr.hash(&mut hasher); + let index = hasher.finish() as usize % listeners.len(); + Some(listeners.swap_remove(index)) + } } } } From 769c6c1d6f193efb4ae9c07f0ff02a02004f0fb4 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 13 Jun 2023 17:47:48 +0000 Subject: [PATCH 42/60] Remove param need_if_watcher from Listener::new --- transports/quic/src/transport.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index 1a3c39fd5ba..36e2a94f3ed 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -197,7 +197,6 @@ impl Transport for GenTransport

{ socket_c, endpoint, self.handshake_timeout, - need_if_watcher, version, )?; self.listeners.push(listener); @@ -421,17 +420,17 @@ impl Listener

{ socket: UdpSocket, endpoint: quinn::Endpoint, handshake_timeout: Duration, - need_if_watcher: bool, version: ProtocolVersion, ) -> Result { let if_watcher; let pending_event; - if need_if_watcher { + let local_addr = socket.local_addr()?; + if local_addr.ip().is_unspecified() { if_watcher = Some(P::new_if_watcher()?); pending_event = None; } else { if_watcher = None; - let ma = socketaddr_to_multiaddr(&socket.local_addr()?, version); + let ma = socketaddr_to_multiaddr(&local_addr, version); pending_event = Some(TransportEvent::NewAddress { listener_id, listen_addr: ma, From d4b8a58dcf39af5b075b1ff19958d3a889e6c047 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 13 Jun 2023 20:09:41 +0000 Subject: [PATCH 43/60] Remove unused variable need_if_watcher --- transports/quic/src/transport.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index 36e2a94f3ed..b97225e2fcd 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -188,7 +188,6 @@ impl Transport for GenTransport

{ let (socket_addr, version, _peer_id) = self.remote_multiaddr_to_socketaddr(addr, false)?; let endpoint_config = self.quinn_config.endpoint_config.clone(); let server_config = self.quinn_config.server_config.clone(); - let need_if_watcher = socket_addr.ip().is_unspecified(); let socket = UdpSocket::bind(socket_addr).map_err(Self::Error::from)?; let socket_c = socket.try_clone().map_err(Self::Error::from)?; let endpoint = Self::new_endpoint(endpoint_config, Some(server_config), socket)?; From 4a0d9bc00f730dc1832f709a6656db64b8ecf0fd Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 14 Jun 2023 09:54:12 +0000 Subject: [PATCH 44/60] Fix poll_read/poll_close on a closed stream --- transports/quic/src/connection/stream.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/transports/quic/src/connection/stream.rs b/transports/quic/src/connection/stream.rs index a93ed555a67..5c909d0a256 100644 --- a/transports/quic/src/connection/stream.rs +++ b/transports/quic/src/connection/stream.rs @@ -32,11 +32,17 @@ pub struct Stream { send: quinn::SendStream, /// A receive part of the stream recv: quinn::RecvStream, + /// Whether the stream is closed or not + closed: bool, } impl Stream { pub(super) fn new(send: quinn::SendStream, recv: quinn::RecvStream) -> Self { - Self { send, recv } + Self { + send, + recv, + closed: false, + } } } @@ -46,6 +52,9 @@ impl AsyncRead for Stream { cx: &mut Context, buf: &mut [u8], ) -> Poll> { + if self.closed { + return Poll::Ready(Ok(0)); + } Pin::new(&mut self.recv).poll_read(cx, buf) } } @@ -64,6 +73,12 @@ impl AsyncWrite for Stream { } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - Pin::new(&mut self.send).poll_close(cx) + if self.closed { + // For some reason poll_close needs to be 'fuse'able + return Poll::Ready(Ok(())); + } + let close_result = futures::ready!(Pin::new(&mut self.send).poll_close(cx)); + self.closed = true; + Poll::Ready(close_result) } } From 038d00cdfcfed7671f6b2be4db0615c95f1475e3 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 14 Jun 2023 09:56:29 +0000 Subject: [PATCH 45/60] Fix dcutr example to bind to 127.0.0.1 instead of 0.0.0.0 --- examples/dcutr/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/dcutr/src/main.rs b/examples/dcutr/src/main.rs index 8359bb1902a..1debc24ce50 100644 --- a/examples/dcutr/src/main.rs +++ b/examples/dcutr/src/main.rs @@ -172,7 +172,7 @@ fn main() -> Result<(), Box> { .build(); swarm - .listen_on("/ip4/0.0.0.0/udp/0/quic-v1".parse().unwrap()) + .listen_on("/ip4/127.0.0.1/udp/0/quic-v1".parse().unwrap()) .unwrap(); swarm .listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap()) From 08f58f9a106824a36568d80cae88d883ed04d552 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 14 Jun 2023 11:56:58 +0000 Subject: [PATCH 46/60] Fix test read_after_peer_dropped_stream --- transports/quic/src/connection/stream.rs | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/transports/quic/src/connection/stream.rs b/transports/quic/src/connection/stream.rs index 5c909d0a256..8e6b32e4e40 100644 --- a/transports/quic/src/connection/stream.rs +++ b/transports/quic/src/connection/stream.rs @@ -33,7 +33,7 @@ pub struct Stream { /// A receive part of the stream recv: quinn::RecvStream, /// Whether the stream is closed or not - closed: bool, + close_result: Option>, } impl Stream { @@ -41,7 +41,7 @@ impl Stream { Self { send, recv, - closed: false, + close_result: None, } } } @@ -52,10 +52,16 @@ impl AsyncRead for Stream { cx: &mut Context, buf: &mut [u8], ) -> Poll> { - if self.closed { + if let Some(close_result) = self.close_result { + if close_result.is_err() { + return Poll::Ready(Ok(0)) + } + } + let read_result = futures::ready!(Pin::new(&mut self.recv).poll_read(cx, buf)); + if read_result.is_err() { return Poll::Ready(Ok(0)); } - Pin::new(&mut self.recv).poll_read(cx, buf) + Poll::Ready(read_result) } } @@ -73,12 +79,16 @@ impl AsyncWrite for Stream { } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - if self.closed { + if let Some(close_result) = self.close_result { // For some reason poll_close needs to be 'fuse'able - return Poll::Ready(Ok(())); + return Poll::Ready(close_result.map_err(|()| io::ErrorKind::Unsupported.into())); } let close_result = futures::ready!(Pin::new(&mut self.send).poll_close(cx)); - self.closed = true; + self.close_result = if close_result.is_ok() { + Some(Ok(())) + } else { + Some(Err(())) + }; Poll::Ready(close_result) } } From fd0187b53ec9d19b1408e7dc13bf13f419301b74 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 14 Jun 2023 12:30:37 +0000 Subject: [PATCH 47/60] cargo fmt --- transports/quic/src/connection/stream.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transports/quic/src/connection/stream.rs b/transports/quic/src/connection/stream.rs index 8e6b32e4e40..30b487225c3 100644 --- a/transports/quic/src/connection/stream.rs +++ b/transports/quic/src/connection/stream.rs @@ -54,7 +54,7 @@ impl AsyncRead for Stream { ) -> Poll> { if let Some(close_result) = self.close_result { if close_result.is_err() { - return Poll::Ready(Ok(0)) + return Poll::Ready(Ok(0)); } } let read_result = futures::ready!(Pin::new(&mut self.recv).poll_read(cx, buf)); From 459dedd3b5d91b2fa7bc84507d2f062b37a6e1e0 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Wed, 28 Jun 2023 15:32:08 +0900 Subject: [PATCH 48/60] fix(quic): use Provider::send_to for UDP datagram --- transports/quic/src/hole_punching.rs | 12 +++++++----- transports/quic/src/provider.rs | 12 ++++++++++++ transports/quic/src/provider/async_std.rs | 13 +++++++++++++ transports/quic/src/provider/tokio.rs | 14 ++++++++++++++ 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/transports/quic/src/hole_punching.rs b/transports/quic/src/hole_punching.rs index 2b4dc81ee15..a168b6bbb49 100644 --- a/transports/quic/src/hole_punching.rs +++ b/transports/quic/src/hole_punching.rs @@ -5,7 +5,6 @@ use futures::future::Either; use rand::{distributions, Rng}; use std::{ - io, net::{SocketAddr, UdpSocket}, time::Duration, }; @@ -24,6 +23,11 @@ pub(crate) async fn hole_puncher( } async fn punch_holes(socket: UdpSocket, remote_addr: SocketAddr) -> Error { + let socket = match P::from_std_udp_socket(socket) { + Ok(s) => s, + Err(e) => return Error::Io(e), + }; + loop { let sleep_duration = Duration::from_millis(rand::thread_rng().gen_range(10..=200)); P::sleep(sleep_duration).await; @@ -33,10 +37,8 @@ async fn punch_holes(socket: UdpSocket, remote_addr: SocketAddr) -> .take(64) .collect(); - if let Err(e) = socket.send_to(&contents, remote_addr) { - if !matches!(e.kind(), io::ErrorKind::WouldBlock) { - return Error::Io(e); - } + if let Err(e) = P::send_to(&socket, &contents, remote_addr).await { + return Error::Io(e); } } } diff --git a/transports/quic/src/provider.rs b/transports/quic/src/provider.rs index 8a2ca62cb44..ded2b6b353f 100644 --- a/transports/quic/src/provider.rs +++ b/transports/quic/src/provider.rs @@ -22,6 +22,7 @@ use futures::{future::BoxFuture, Future}; use if_watch::IfEvent; use std::{ io, + net::SocketAddr, task::{Context, Poll}, time::Duration, }; @@ -42,6 +43,7 @@ pub enum Runtime { /// Provider for a corresponding quinn runtime and spawning tasks. pub trait Provider: Unpin + Send + Sized + 'static { type IfWatcher: Unpin + Send; + type UdpSocket: Unpin + Send + Sync; /// Run the corresponding runtime fn runtime() -> Runtime; @@ -62,4 +64,14 @@ pub trait Provider: Unpin + Send + Sized + 'static { /// Sleep for specified amount of time. fn sleep(duration: Duration) -> BoxFuture<'static, ()>; + + /// Creates new UdpSocket from a previously bound std::net::UdpSocket. + fn from_std_udp_socket(socket: std::net::UdpSocket) -> io::Result; + + /// Sends data on the socket to the given address. On success, returns the number of bytes written. + fn send_to<'a>( + udp_socket: &'a Self::UdpSocket, + buf: &'a [u8], + target: SocketAddr, + ) -> BoxFuture<'a, io::Result>; } diff --git a/transports/quic/src/provider/async_std.rs b/transports/quic/src/provider/async_std.rs index da28727aed1..57a19ad4fc8 100644 --- a/transports/quic/src/provider/async_std.rs +++ b/transports/quic/src/provider/async_std.rs @@ -36,6 +36,7 @@ pub struct Provider; impl super::Provider for Provider { type IfWatcher = if_watch::smol::IfWatcher; + type UdpSocket = async_std::net::UdpSocket; fn runtime() -> super::Runtime { super::Runtime::AsyncStd @@ -59,4 +60,16 @@ impl super::Provider for Provider { fn sleep(duration: Duration) -> BoxFuture<'static, ()> { async_std::task::sleep(duration).boxed() } + + fn from_std_udp_socket(socket: std::net::UdpSocket) -> io::Result { + Ok(socket.into()) + } + + fn send_to<'a>( + udp_socket: &'a Self::UdpSocket, + buf: &'a [u8], + target: std::net::SocketAddr, + ) -> BoxFuture<'a, io::Result> { + udp_socket.send_to(buf, target).boxed() + } } diff --git a/transports/quic/src/provider/tokio.rs b/transports/quic/src/provider/tokio.rs index 7accc3ce60b..50ebb836228 100644 --- a/transports/quic/src/provider/tokio.rs +++ b/transports/quic/src/provider/tokio.rs @@ -21,6 +21,7 @@ use futures::{future::BoxFuture, Future, FutureExt}; use std::{ io, + net::SocketAddr, task::{Context, Poll}, time::Duration, }; @@ -35,6 +36,7 @@ pub struct Provider; impl super::Provider for Provider { type IfWatcher = if_watch::tokio::IfWatcher; + type UdpSocket = tokio::net::UdpSocket; fn runtime() -> super::Runtime { super::Runtime::Tokio @@ -58,4 +60,16 @@ impl super::Provider for Provider { fn sleep(duration: Duration) -> BoxFuture<'static, ()> { tokio::time::sleep(duration).boxed() } + + fn from_std_udp_socket(socket: std::net::UdpSocket) -> io::Result { + tokio::net::UdpSocket::from_std(socket) + } + + fn send_to<'a>( + udp_socket: &'a Self::UdpSocket, + buf: &'a [u8], + target: SocketAddr, + ) -> BoxFuture<'a, io::Result> { + udp_socket.send_to(buf, target).boxed() + } } From bae498375d6d0e724037122262a35a4eb2b125b4 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 28 Jun 2023 09:32:10 +0000 Subject: [PATCH 49/60] Clone ErrorKind in Stream::poll_close --- transports/quic/src/connection/stream.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/transports/quic/src/connection/stream.rs b/transports/quic/src/connection/stream.rs index 30b487225c3..d317c81b9d3 100644 --- a/transports/quic/src/connection/stream.rs +++ b/transports/quic/src/connection/stream.rs @@ -33,7 +33,7 @@ pub struct Stream { /// A receive part of the stream recv: quinn::RecvStream, /// Whether the stream is closed or not - close_result: Option>, + close_result: Option>, } impl Stream { @@ -79,16 +79,12 @@ impl AsyncWrite for Stream { } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - if let Some(close_result) = self.close_result { + if let Some(close_result) = self.close_result.clone() { // For some reason poll_close needs to be 'fuse'able - return Poll::Ready(close_result.map_err(|()| io::ErrorKind::Unsupported.into())); + return Poll::Ready(close_result.map_err(Into::into)); } let close_result = futures::ready!(Pin::new(&mut self.send).poll_close(cx)); - self.close_result = if close_result.is_ok() { - Some(Ok(())) - } else { - Some(Err(())) - }; + self.close_result = Some(close_result.as_ref().map_err(|e| e.kind()).copied()); Poll::Ready(close_result) } } From dd619ca5478636692cb2be9e51e64ce61cea5e29 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 28 Jun 2023 10:08:14 +0000 Subject: [PATCH 50/60] cargo clippy --- transports/quic/src/connection/stream.rs | 2 +- transports/quic/src/transport.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/transports/quic/src/connection/stream.rs b/transports/quic/src/connection/stream.rs index d317c81b9d3..d6c07d90821 100644 --- a/transports/quic/src/connection/stream.rs +++ b/transports/quic/src/connection/stream.rs @@ -79,7 +79,7 @@ impl AsyncWrite for Stream { } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - if let Some(close_result) = self.close_result.clone() { + if let Some(close_result) = self.close_result { // For some reason poll_close needs to be 'fuse'able return Poll::Ready(close_result.map_err(Into::into)); } diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index b97225e2fcd..d4a1db35604 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -696,7 +696,7 @@ fn is_quic_addr(addr: &Multiaddr, support_draft_29: bool) -> bool { matches!(third, QuicV1) } && matches!(fourth, Some(P2p(_)) | None) - && matches!(fifth, None) + && fifth.is_none() } /// Turns an IP address and port into the corresponding QUIC multiaddr. From f31da2e137282450674765e6ca16a4a349251d8f Mon Sep 17 00:00:00 2001 From: Max Inden Date: Sun, 2 Jul 2023 14:35:16 +0900 Subject: [PATCH 51/60] Simplify and hide dependency types --- transports/quic/src/hole_punching.rs | 5 ----- transports/quic/src/provider.rs | 8 ++------ transports/quic/src/provider/async_std.rs | 14 +++++++------- transports/quic/src/provider/tokio.rs | 15 +++++++-------- 4 files changed, 16 insertions(+), 26 deletions(-) diff --git a/transports/quic/src/hole_punching.rs b/transports/quic/src/hole_punching.rs index a168b6bbb49..c1e9a447c6d 100644 --- a/transports/quic/src/hole_punching.rs +++ b/transports/quic/src/hole_punching.rs @@ -23,11 +23,6 @@ pub(crate) async fn hole_puncher( } async fn punch_holes(socket: UdpSocket, remote_addr: SocketAddr) -> Error { - let socket = match P::from_std_udp_socket(socket) { - Ok(s) => s, - Err(e) => return Error::Io(e), - }; - loop { let sleep_duration = Duration::from_millis(rand::thread_rng().gen_range(10..=200)); P::sleep(sleep_duration).await; diff --git a/transports/quic/src/provider.rs b/transports/quic/src/provider.rs index ded2b6b353f..26e9e35902f 100644 --- a/transports/quic/src/provider.rs +++ b/transports/quic/src/provider.rs @@ -22,7 +22,7 @@ use futures::{future::BoxFuture, Future}; use if_watch::IfEvent; use std::{ io, - net::SocketAddr, + net::{SocketAddr, UdpSocket}, task::{Context, Poll}, time::Duration, }; @@ -43,7 +43,6 @@ pub enum Runtime { /// Provider for a corresponding quinn runtime and spawning tasks. pub trait Provider: Unpin + Send + Sized + 'static { type IfWatcher: Unpin + Send; - type UdpSocket: Unpin + Send + Sync; /// Run the corresponding runtime fn runtime() -> Runtime; @@ -65,12 +64,9 @@ pub trait Provider: Unpin + Send + Sized + 'static { /// Sleep for specified amount of time. fn sleep(duration: Duration) -> BoxFuture<'static, ()>; - /// Creates new UdpSocket from a previously bound std::net::UdpSocket. - fn from_std_udp_socket(socket: std::net::UdpSocket) -> io::Result; - /// Sends data on the socket to the given address. On success, returns the number of bytes written. fn send_to<'a>( - udp_socket: &'a Self::UdpSocket, + udp_socket: &'a UdpSocket, buf: &'a [u8], target: SocketAddr, ) -> BoxFuture<'a, io::Result>; diff --git a/transports/quic/src/provider/async_std.rs b/transports/quic/src/provider/async_std.rs index 57a19ad4fc8..4721cba9b32 100644 --- a/transports/quic/src/provider/async_std.rs +++ b/transports/quic/src/provider/async_std.rs @@ -22,6 +22,7 @@ use async_std::task::spawn; use futures::{future::BoxFuture, Future, FutureExt}; use std::{ io, + net::UdpSocket, task::{Context, Poll}, time::Duration, }; @@ -36,7 +37,6 @@ pub struct Provider; impl super::Provider for Provider { type IfWatcher = if_watch::smol::IfWatcher; - type UdpSocket = async_std::net::UdpSocket; fn runtime() -> super::Runtime { super::Runtime::AsyncStd @@ -61,15 +61,15 @@ impl super::Provider for Provider { async_std::task::sleep(duration).boxed() } - fn from_std_udp_socket(socket: std::net::UdpSocket) -> io::Result { - Ok(socket.into()) - } - fn send_to<'a>( - udp_socket: &'a Self::UdpSocket, + udp_socket: &'a UdpSocket, buf: &'a [u8], target: std::net::SocketAddr, ) -> BoxFuture<'a, io::Result> { - udp_socket.send_to(buf, target).boxed() + Box::pin(async move { + async_std::net::UdpSocket::from(udp_socket.try_clone()?) + .send_to(buf, target) + .await + }) } } diff --git a/transports/quic/src/provider/tokio.rs b/transports/quic/src/provider/tokio.rs index 50ebb836228..b32f7ee184f 100644 --- a/transports/quic/src/provider/tokio.rs +++ b/transports/quic/src/provider/tokio.rs @@ -21,7 +21,7 @@ use futures::{future::BoxFuture, Future, FutureExt}; use std::{ io, - net::SocketAddr, + net::{SocketAddr, UdpSocket}, task::{Context, Poll}, time::Duration, }; @@ -36,7 +36,6 @@ pub struct Provider; impl super::Provider for Provider { type IfWatcher = if_watch::tokio::IfWatcher; - type UdpSocket = tokio::net::UdpSocket; fn runtime() -> super::Runtime { super::Runtime::Tokio @@ -61,15 +60,15 @@ impl super::Provider for Provider { tokio::time::sleep(duration).boxed() } - fn from_std_udp_socket(socket: std::net::UdpSocket) -> io::Result { - tokio::net::UdpSocket::from_std(socket) - } - fn send_to<'a>( - udp_socket: &'a Self::UdpSocket, + udp_socket: &'a UdpSocket, buf: &'a [u8], target: SocketAddr, ) -> BoxFuture<'a, io::Result> { - udp_socket.send_to(buf, target).boxed() + Box::pin(async move { + tokio::net::UdpSocket::from_std(udp_socket.try_clone()?)? + .send_to(buf, target) + .await + }) } } From bde3252eccab9dd336236f5439f17e5b4a5c54cf Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 7 Jul 2023 11:42:31 +0900 Subject: [PATCH 52/60] fix(quic/stream): return error on read --- transports/quic/src/connection/stream.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/transports/quic/src/connection/stream.rs b/transports/quic/src/connection/stream.rs index d6c07d90821..b0c505bf856 100644 --- a/transports/quic/src/connection/stream.rs +++ b/transports/quic/src/connection/stream.rs @@ -57,11 +57,7 @@ impl AsyncRead for Stream { return Poll::Ready(Ok(0)); } } - let read_result = futures::ready!(Pin::new(&mut self.recv).poll_read(cx, buf)); - if read_result.is_err() { - return Poll::Ready(Ok(0)); - } - Poll::Ready(read_result) + Pin::new(&mut self.recv).poll_read(cx, buf) } } From 7505c6569147e5ff42f276b93b3b16f9b0dd189f Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 7 Jul 2023 12:19:52 +0900 Subject: [PATCH 53/60] refactor(quic/provider): remove Provider::spawn Given that it is only used in tests, there is no need for `Provider::spawn`. Instead a new test-only `Spawn` trait is created, implemented by each of the providers. --- transports/quic/src/provider.rs | 7 +----- transports/quic/src/provider/async_std.rs | 7 +----- transports/quic/src/provider/tokio.rs | 6 +---- transports/quic/tests/smoke.rs | 27 +++++++++++++++++++---- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/transports/quic/src/provider.rs b/transports/quic/src/provider.rs index 26e9e35902f..6f1122ee55f 100644 --- a/transports/quic/src/provider.rs +++ b/transports/quic/src/provider.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use futures::{future::BoxFuture, Future}; +use futures::future::BoxFuture; use if_watch::IfEvent; use std::{ io, @@ -47,11 +47,6 @@ pub trait Provider: Unpin + Send + Sized + 'static { /// Run the corresponding runtime fn runtime() -> Runtime; - /// Run the given future in the background until it ends. - /// - /// This is used to spawn the task that is driving the endpoint. - fn spawn(future: impl Future + Send + 'static); - /// Create a new [`if_watch`] watcher that reports [`IfEvent`]s for network interface changes. fn new_if_watcher() -> io::Result; diff --git a/transports/quic/src/provider/async_std.rs b/transports/quic/src/provider/async_std.rs index 4721cba9b32..a110058108c 100644 --- a/transports/quic/src/provider/async_std.rs +++ b/transports/quic/src/provider/async_std.rs @@ -18,8 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use async_std::task::spawn; -use futures::{future::BoxFuture, Future, FutureExt}; +use futures::{future::BoxFuture, FutureExt}; use std::{ io, net::UdpSocket, @@ -42,10 +41,6 @@ impl super::Provider for Provider { super::Runtime::AsyncStd } - fn spawn(future: impl Future + Send + 'static) { - spawn(future); - } - fn new_if_watcher() -> io::Result { if_watch::smol::IfWatcher::new() } diff --git a/transports/quic/src/provider/tokio.rs b/transports/quic/src/provider/tokio.rs index b32f7ee184f..9cb148d6ef2 100644 --- a/transports/quic/src/provider/tokio.rs +++ b/transports/quic/src/provider/tokio.rs @@ -18,7 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use futures::{future::BoxFuture, Future, FutureExt}; +use futures::{future::BoxFuture, FutureExt}; use std::{ io, net::{SocketAddr, UdpSocket}, @@ -41,10 +41,6 @@ impl super::Provider for Provider { super::Runtime::Tokio } - fn spawn(future: impl Future + Send + 'static) { - tokio::spawn(future); - } - fn new_if_watcher() -> io::Result { if_watch::tokio::IfWatcher::new() } diff --git a/transports/quic/tests/smoke.rs b/transports/quic/tests/smoke.rs index 8a6d689a7b0..93adfa68013 100644 --- a/transports/quic/tests/smoke.rs +++ b/transports/quic/tests/smoke.rs @@ -428,7 +428,7 @@ async fn smoke() { assert_eq!(b_connected, a_peer_id); } -async fn build_streams() -> (SubstreamBox, SubstreamBox) { +async fn build_streams() -> (SubstreamBox, SubstreamBox) { let (_, mut a_transport) = create_default_transport::

(); let (_, mut b_transport) = create_default_transport::

(); @@ -522,7 +522,7 @@ async fn start_listening(transport: &mut Boxed<(PeerId, StreamMuxerBox)>, addr: } } -fn prop( +fn prop( number_listeners: NonZeroU8, number_streams: NonZeroU8, ) -> quickcheck::TestResult { @@ -599,7 +599,7 @@ fn prop( quickcheck::TestResult::passed() } -async fn answer_inbound_streams( +async fn answer_inbound_streams( mut connection: StreamMuxerBox, ) { loop { @@ -634,7 +634,7 @@ async fn answer_inbound_streams( } } -async fn open_outbound_streams( +async fn open_outbound_streams( mut connection: StreamMuxerBox, number_streams: usize, completed_streams_tx: mpsc::Sender<()>, @@ -740,3 +740,22 @@ impl BlockOn for libp2p_quic::tokio::Provider { .unwrap() } } + +trait Spawn { + /// Run the given future in the background until it ends. + fn spawn(future: impl Future + Send + 'static); +} + +#[cfg(feature = "async-std")] +impl Spawn for libp2p_quic::async_std::Provider { + fn spawn(future: impl Future + Send + 'static) { + async_std::task::spawn(future); + } +} + +#[cfg(feature = "tokio")] +impl Spawn for libp2p_quic::tokio::Provider { + fn spawn(future: impl Future + Send + 'static) { + tokio::spawn(future); + } +} From d977cb511ba2e4cb5ac726549f1ac2dafb8116d6 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 7 Jul 2023 12:25:50 +0900 Subject: [PATCH 54/60] fix(Cargo.lock): udpate --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8e725f340e6..80200234f10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4266,7 +4266,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.21.1", + "rustls 0.21.2", "thiserror", "tokio", "tracing", @@ -4299,7 +4299,7 @@ dependencies = [ "libc", "socket2 0.5.3", "tracing", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] From 8b651051fc40250bc68758cd28850e4830fe0512 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Mon, 24 Jul 2023 21:14:01 +0200 Subject: [PATCH 55/60] fix(quic/connection): await connection.closed --- transports/quic/src/connection.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/transports/quic/src/connection.rs b/transports/quic/src/connection.rs index 70cc1a9c518..783258a0130 100644 --- a/transports/quic/src/connection.rs +++ b/transports/quic/src/connection.rs @@ -45,6 +45,8 @@ pub struct Connection { outgoing: Option< BoxFuture<'static, Result<(quinn::SendStream, quinn::RecvStream), quinn::ConnectionError>>, >, + /// Future to wait for the connection to be closed. + closing: Option>, } impl Connection { @@ -57,6 +59,7 @@ impl Connection { connection, incoming: None, outgoing: None, + closing: None, } } } @@ -108,8 +111,21 @@ impl StreamMuxer for Connection { Poll::Pending } - fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - self.connection.close(From::from(0u32), &[]); + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.get_mut(); + + let closing = this.closing.get_or_insert_with(|| { + this.connection.close(From::from(0u32), &[]); + let connection = this.connection.clone(); + async move { connection.closed().await }.boxed() + }); + + match futures::ready!(closing.poll_unpin(cx)) { + // Expected error given that `connection.close` was called above. + quinn::ConnectionError::LocallyClosed => {} + error => return Poll::Ready(Err(Error::Connection(ConnectionError(error)))), + }; + Poll::Ready(Ok(())) } } From 0b0877080faeba5541802a0b8297caf8d3945263 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 27 Jul 2023 17:39:49 +0200 Subject: [PATCH 56/60] Revert back to 0.0.0.0 listen_on in dcutr example --- examples/dcutr/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/dcutr/src/main.rs b/examples/dcutr/src/main.rs index 1debc24ce50..8359bb1902a 100644 --- a/examples/dcutr/src/main.rs +++ b/examples/dcutr/src/main.rs @@ -172,7 +172,7 @@ fn main() -> Result<(), Box> { .build(); swarm - .listen_on("/ip4/127.0.0.1/udp/0/quic-v1".parse().unwrap()) + .listen_on("/ip4/0.0.0.0/udp/0/quic-v1".parse().unwrap()) .unwrap(); swarm .listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap()) From b6bd51fbd3a31ba9f0308a16c015e53d9b52bb52 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 27 Jul 2023 17:45:00 +0200 Subject: [PATCH 57/60] Bump version --- Cargo.lock | 2 +- Cargo.toml | 2 +- transports/quic/CHANGELOG.md | 7 +++++++ transports/quic/Cargo.toml | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7766c9ac268..7bb723a3d72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3024,7 +3024,7 @@ dependencies = [ [[package]] name = "libp2p-quic" -version = "0.8.0-alpha" +version = "0.9.0-alpha" dependencies = [ "async-std", "bytes", diff --git a/Cargo.toml b/Cargo.toml index eff66e42986..aa01895b090 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,7 +83,7 @@ libp2p-perf = { version = "0.2.0", path = "protocols/perf" } libp2p-ping = { version = "0.43.0", path = "protocols/ping" } libp2p-plaintext = { version = "0.40.0", path = "transports/plaintext" } libp2p-pnet = { version = "0.23.0", path = "transports/pnet" } -libp2p-quic = { version = "0.8.0-alpha", path = "transports/quic" } +libp2p-quic = { version = "0.9.0-alpha", path = "transports/quic" } libp2p-relay = { version = "0.16.0", path = "protocols/relay" } libp2p-rendezvous = { version = "0.13.0", path = "protocols/rendezvous" } libp2p-request-response = { version = "0.25.0", path = "protocols/request-response" } diff --git a/transports/quic/CHANGELOG.md b/transports/quic/CHANGELOG.md index 6e3ce801a2c..2012a3caf94 100644 --- a/transports/quic/CHANGELOG.md +++ b/transports/quic/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.9.0-alpha - unreleased + +- Use `quinn` instead of `quinn-proto`. + See [PR 3454]. + +[PR 3454]: https://github.com/libp2p/rust-libp2p/pull/3454 + ## 0.8.0-alpha - Raise MSRV to 1.65. diff --git a/transports/quic/Cargo.toml b/transports/quic/Cargo.toml index d9bec17d097..dafc828ca0f 100644 --- a/transports/quic/Cargo.toml +++ b/transports/quic/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libp2p-quic" -version = "0.8.0-alpha" +version = "0.9.0-alpha" authors = ["Parity Technologies "] edition = "2021" rust-version = { workspace = true } From fccd97925f25a885a574b0b4322fef1e34cc59e7 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 27 Jul 2023 17:49:43 +0200 Subject: [PATCH 58/60] Try hiding Provider trait --- transports/quic/src/hole_punching.rs | 2 +- transports/quic/src/lib.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/transports/quic/src/hole_punching.rs b/transports/quic/src/hole_punching.rs index c1e9a447c6d..874bc659b2e 100644 --- a/transports/quic/src/hole_punching.rs +++ b/transports/quic/src/hole_punching.rs @@ -1,4 +1,4 @@ -use crate::{Error, Provider}; +use crate::{provider::Provider, Error}; use futures::future::Either; diff --git a/transports/quic/src/lib.rs b/transports/quic/src/lib.rs index f51f5ee4804..aca9eef1c2f 100644 --- a/transports/quic/src/lib.rs +++ b/transports/quic/src/lib.rs @@ -72,7 +72,6 @@ pub use connection::{Connecting, Connection, Stream}; pub use provider::async_std; #[cfg(feature = "tokio")] pub use provider::tokio; -pub use provider::Provider; pub use transport::GenTransport; /// Errors that may happen on the [`GenTransport`] or a single [`Connection`]. @@ -90,7 +89,7 @@ pub enum Error { #[error(transparent)] Io(#[from] std::io::Error), - /// The task spawned in [`Provider::spawn`] to drive + /// The task spawned in [`provider::Provider::spawn`] to drive /// the quic endpoint has crashed. #[error("Endpoint driver crashed")] EndpointDriverCrashed, From cfd147e7ed799b4f2179bd43f49373ff0e7bc63b Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 27 Jul 2023 17:55:30 +0200 Subject: [PATCH 59/60] Fix doc comment --- transports/quic/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/transports/quic/src/lib.rs b/transports/quic/src/lib.rs index aca9eef1c2f..896c66b9dfb 100644 --- a/transports/quic/src/lib.rs +++ b/transports/quic/src/lib.rs @@ -89,8 +89,7 @@ pub enum Error { #[error(transparent)] Io(#[from] std::io::Error), - /// The task spawned in [`provider::Provider::spawn`] to drive - /// the quic endpoint has crashed. + /// The task to drive a quic endpoint has crashed. #[error("Endpoint driver crashed")] EndpointDriverCrashed, From a92954a503dcaa189835a57d89cb7d3594816dde Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 28 Jul 2023 11:57:46 +0200 Subject: [PATCH 60/60] Revert hiding Provider trait --- transports/quic/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/transports/quic/src/lib.rs b/transports/quic/src/lib.rs index 896c66b9dfb..494ecfdcddb 100644 --- a/transports/quic/src/lib.rs +++ b/transports/quic/src/lib.rs @@ -72,6 +72,7 @@ pub use connection::{Connecting, Connection, Stream}; pub use provider::async_std; #[cfg(feature = "tokio")] pub use provider::tokio; +pub use provider::Provider; pub use transport::GenTransport; /// Errors that may happen on the [`GenTransport`] or a single [`Connection`].