2022-11-14 16:12:16 +04:00
|
|
|
#![cfg(any(feature = "async-std", feature = "tokio"))]
|
|
|
|
|
2022-11-25 10:05:50 +01:00
|
|
|
use futures::channel::{mpsc, oneshot};
|
2023-01-24 00:39:13 +01:00
|
|
|
use futures::future::BoxFuture;
|
2022-11-25 10:05:50 +01:00
|
|
|
use futures::future::{poll_fn, Either};
|
2022-11-14 16:12:16 +04:00
|
|
|
use futures::stream::StreamExt;
|
2022-11-25 10:05:50 +01:00
|
|
|
use futures::{future, AsyncReadExt, AsyncWriteExt, FutureExt, SinkExt};
|
2023-01-24 00:39:13 +01:00
|
|
|
use futures_timer::Delay;
|
2022-11-25 10:05:50 +01:00
|
|
|
use libp2p_core::muxing::{StreamMuxerBox, StreamMuxerExt, SubstreamBox};
|
2022-11-14 16:12:16 +04:00
|
|
|
use libp2p_core::transport::{Boxed, OrTransport, TransportEvent};
|
2023-01-24 00:39:13 +01:00
|
|
|
use libp2p_core::transport::{ListenerId, TransportError};
|
2022-12-13 07:58:01 +11:00
|
|
|
use libp2p_core::{multiaddr::Protocol, upgrade, Multiaddr, PeerId, Transport};
|
|
|
|
use libp2p_noise as noise;
|
2022-11-14 16:12:16 +04:00
|
|
|
use libp2p_quic as quic;
|
2022-12-13 07:58:01 +11:00
|
|
|
use libp2p_tcp as tcp;
|
|
|
|
use libp2p_yamux as yamux;
|
2022-11-14 16:12:16 +04:00
|
|
|
use quic::Provider;
|
|
|
|
use rand::RngCore;
|
|
|
|
use std::future::Future;
|
|
|
|
use std::io;
|
|
|
|
use std::num::NonZeroU8;
|
2022-11-25 10:05:50 +01:00
|
|
|
use std::task::Poll;
|
2022-11-14 16:12:16 +04:00
|
|
|
use std::time::Duration;
|
2023-01-24 00:39:13 +01:00
|
|
|
use std::{
|
|
|
|
pin::Pin,
|
|
|
|
sync::{Arc, Mutex},
|
|
|
|
};
|
2022-11-14 16:12:16 +04:00
|
|
|
|
|
|
|
#[cfg(feature = "tokio")]
|
|
|
|
#[tokio::test]
|
|
|
|
async fn tokio_smoke() {
|
|
|
|
smoke::<quic::tokio::Provider>().await
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "async-std")]
|
|
|
|
#[async_std::test]
|
|
|
|
async fn async_std_smoke() {
|
|
|
|
smoke::<quic::async_std::Provider>().await
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "async-std")]
|
|
|
|
#[async_std::test]
|
|
|
|
async fn dial_failure() {
|
|
|
|
let _ = env_logger::try_init();
|
2022-12-02 20:43:04 +01:00
|
|
|
let mut a = create_default_transport::<quic::async_std::Provider>().1;
|
|
|
|
let mut b = create_default_transport::<quic::async_std::Provider>().1;
|
2022-11-14 16:12:16 +04:00
|
|
|
|
|
|
|
let addr = start_listening(&mut a, "/ip4/127.0.0.1/udp/0/quic-v1").await;
|
|
|
|
drop(a); // stop a so b can never reach it
|
|
|
|
|
|
|
|
match dial(&mut b, addr).await {
|
|
|
|
Ok(_) => panic!("Expected dial to fail"),
|
|
|
|
Err(error) => {
|
|
|
|
assert_eq!("Handshake with the remote timed out.", error.to_string())
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "tokio")]
|
|
|
|
#[tokio::test]
|
|
|
|
async fn endpoint_reuse() {
|
|
|
|
let _ = env_logger::try_init();
|
2022-12-02 20:43:04 +01:00
|
|
|
let (_, mut a_transport) = create_default_transport::<quic::tokio::Provider>();
|
|
|
|
let (_, mut b_transport) = create_default_transport::<quic::tokio::Provider>();
|
2022-11-14 16:12:16 +04:00
|
|
|
|
|
|
|
let a_addr = start_listening(&mut a_transport, "/ip4/127.0.0.1/udp/0/quic-v1").await;
|
|
|
|
let ((_, b_send_back_addr, _), _) =
|
|
|
|
connect(&mut a_transport, &mut b_transport, a_addr.clone()).await;
|
|
|
|
|
|
|
|
// Expect the dial to fail since b is not listening on an address.
|
|
|
|
match dial(&mut a_transport, b_send_back_addr).await {
|
|
|
|
Ok(_) => panic!("Expected dial to fail"),
|
|
|
|
Err(error) => {
|
|
|
|
assert_eq!("Handshake with the remote timed out.", error.to_string())
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let b_addr = start_listening(&mut b_transport, "/ip4/127.0.0.1/udp/0/quic-v1").await;
|
|
|
|
let ((_, a_send_back_addr, _), _) = connect(&mut b_transport, &mut a_transport, b_addr).await;
|
|
|
|
|
|
|
|
assert_eq!(a_send_back_addr, a_addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "async-std")]
|
|
|
|
#[async_std::test]
|
|
|
|
async fn ipv4_dial_ipv6() {
|
|
|
|
let _ = env_logger::try_init();
|
2022-12-02 20:43:04 +01:00
|
|
|
let (a_peer_id, mut a_transport) = create_default_transport::<quic::async_std::Provider>();
|
|
|
|
let (b_peer_id, mut b_transport) = create_default_transport::<quic::async_std::Provider>();
|
2022-11-14 16:12:16 +04:00
|
|
|
|
|
|
|
let a_addr = start_listening(&mut a_transport, "/ip6/::1/udp/0/quic-v1").await;
|
|
|
|
let ((a_connected, _, _), (b_connected, _)) =
|
|
|
|
connect(&mut a_transport, &mut b_transport, a_addr).await;
|
|
|
|
|
|
|
|
assert_eq!(a_connected, b_peer_id);
|
|
|
|
assert_eq!(b_connected, a_peer_id);
|
|
|
|
}
|
|
|
|
|
2023-01-24 00:39:13 +01:00
|
|
|
/// Tests that a [`Transport::dial`] wakes up the task previously polling [`Transport::poll`].
|
|
|
|
///
|
|
|
|
/// See https://github.com/libp2p/rust-libp2p/pull/3306 for context.
|
|
|
|
#[cfg(feature = "async-std")]
|
|
|
|
#[async_std::test]
|
|
|
|
async fn wrapped_with_delay() {
|
|
|
|
let _ = env_logger::try_init();
|
|
|
|
|
|
|
|
struct DialDelay(Arc<Mutex<Boxed<(PeerId, StreamMuxerBox)>>>);
|
|
|
|
|
|
|
|
impl Transport for DialDelay {
|
|
|
|
type Output = (PeerId, StreamMuxerBox);
|
|
|
|
type Error = std::io::Error;
|
|
|
|
type ListenerUpgrade = Pin<Box<dyn Future<Output = io::Result<Self::Output>> + Send>>;
|
|
|
|
type Dial = BoxFuture<'static, Result<Self::Output, Self::Error>>;
|
|
|
|
|
|
|
|
fn listen_on(
|
|
|
|
&mut self,
|
|
|
|
addr: Multiaddr,
|
|
|
|
) -> Result<ListenerId, TransportError<Self::Error>> {
|
|
|
|
self.0.lock().unwrap().listen_on(addr)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn remove_listener(&mut self, id: ListenerId) -> bool {
|
|
|
|
self.0.lock().unwrap().remove_listener(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn address_translation(
|
|
|
|
&self,
|
|
|
|
listen: &Multiaddr,
|
|
|
|
observed: &Multiaddr,
|
|
|
|
) -> Option<Multiaddr> {
|
|
|
|
self.0.lock().unwrap().address_translation(listen, observed)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Delayed dial, i.e. calling [`Transport::dial`] on the inner [`Transport`] not within the
|
|
|
|
/// synchronous [`Transport::dial`] method, but within the [`Future`] returned by the outer
|
|
|
|
/// [`Transport::dial`].
|
|
|
|
fn dial(&mut self, addr: Multiaddr) -> Result<Self::Dial, TransportError<Self::Error>> {
|
|
|
|
let t = self.0.clone();
|
|
|
|
Ok(async move {
|
|
|
|
// Simulate DNS lookup. Giving the `Transport::poll` the chance to return
|
|
|
|
// `Poll::Pending` and thus suspending its task, waiting for a wakeup from the dial
|
|
|
|
// on the inner transport below.
|
|
|
|
Delay::new(Duration::from_millis(100)).await;
|
|
|
|
|
|
|
|
let dial = t.lock().unwrap().dial(addr).map_err(|e| match e {
|
|
|
|
TransportError::MultiaddrNotSupported(_) => {
|
|
|
|
panic!()
|
|
|
|
}
|
|
|
|
TransportError::Other(e) => e,
|
|
|
|
})?;
|
|
|
|
dial.await
|
|
|
|
}
|
|
|
|
.boxed())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn dial_as_listener(
|
|
|
|
&mut self,
|
|
|
|
addr: Multiaddr,
|
|
|
|
) -> Result<Self::Dial, TransportError<Self::Error>> {
|
|
|
|
self.0.lock().unwrap().dial_as_listener(addr)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn poll(
|
|
|
|
self: Pin<&mut Self>,
|
|
|
|
cx: &mut std::task::Context<'_>,
|
|
|
|
) -> Poll<TransportEvent<Self::ListenerUpgrade, Self::Error>> {
|
|
|
|
Pin::new(&mut *self.0.lock().unwrap()).poll(cx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let (a_peer_id, mut a_transport) = create_default_transport::<quic::async_std::Provider>();
|
|
|
|
let (b_peer_id, mut b_transport) = {
|
|
|
|
let (id, transport) = create_default_transport::<quic::async_std::Provider>();
|
|
|
|
(id, DialDelay(Arc::new(Mutex::new(transport))).boxed())
|
|
|
|
};
|
|
|
|
|
|
|
|
// Spawn A
|
|
|
|
let a_addr = start_listening(&mut a_transport, "/ip6/::1/udp/0/quic-v1").await;
|
|
|
|
let listener = async_std::task::spawn(async move {
|
|
|
|
let (upgrade, _) = a_transport
|
|
|
|
.select_next_some()
|
|
|
|
.await
|
|
|
|
.into_incoming()
|
|
|
|
.unwrap();
|
|
|
|
let (peer_id, _) = upgrade.await.unwrap();
|
|
|
|
|
|
|
|
peer_id
|
|
|
|
});
|
|
|
|
|
|
|
|
// Spawn B
|
|
|
|
//
|
|
|
|
// Note that the dial is spawned on a different task than the transport allowing the transport
|
|
|
|
// task to poll the transport once and then suspend, waiting for the wakeup from the dial.
|
|
|
|
let dial = async_std::task::spawn({
|
|
|
|
let dial = b_transport.dial(a_addr).unwrap();
|
|
|
|
async { dial.await.unwrap().0 }
|
|
|
|
});
|
|
|
|
async_std::task::spawn(async move { b_transport.next().await });
|
|
|
|
|
|
|
|
let (a_connected, b_connected) = future::join(listener, dial).await;
|
|
|
|
|
|
|
|
assert_eq!(a_connected, b_peer_id);
|
|
|
|
assert_eq!(b_connected, a_peer_id);
|
|
|
|
}
|
|
|
|
|
2022-11-14 16:12:16 +04:00
|
|
|
#[cfg(feature = "async-std")]
|
|
|
|
#[async_std::test]
|
|
|
|
#[ignore] // Transport currently does not validate PeerId. Enable once we make use of PeerId validation in rustls.
|
|
|
|
async fn wrong_peerid() {
|
2022-12-13 07:58:01 +11:00
|
|
|
use libp2p_core::PeerId;
|
2022-11-14 16:12:16 +04:00
|
|
|
|
2022-12-02 20:43:04 +01:00
|
|
|
let (a_peer_id, mut a_transport) = create_default_transport::<quic::async_std::Provider>();
|
|
|
|
let (b_peer_id, mut b_transport) = create_default_transport::<quic::async_std::Provider>();
|
2022-11-14 16:12:16 +04:00
|
|
|
|
|
|
|
let a_addr = start_listening(&mut a_transport, "/ip6/::1/udp/0/quic-v1").await;
|
|
|
|
let a_addr_random_peer = a_addr.with(Protocol::P2p(PeerId::random().into()));
|
|
|
|
|
|
|
|
let ((a_connected, _, _), (b_connected, _)) =
|
|
|
|
connect(&mut a_transport, &mut b_transport, a_addr_random_peer).await;
|
|
|
|
|
|
|
|
assert_ne!(a_connected, b_peer_id);
|
|
|
|
assert_eq!(b_connected, a_peer_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "async-std")]
|
|
|
|
fn new_tcp_quic_transport() -> (PeerId, Boxed<(PeerId, StreamMuxerBox)>) {
|
|
|
|
let keypair = generate_tls_keypair();
|
|
|
|
let peer_id = keypair.public().to_peer_id();
|
|
|
|
let mut config = quic::Config::new(&keypair);
|
|
|
|
config.handshake_timeout = Duration::from_secs(1);
|
|
|
|
|
|
|
|
let quic_transport = quic::async_std::Transport::new(config);
|
|
|
|
let tcp_transport = tcp::async_io::Transport::new(tcp::Config::default())
|
|
|
|
.upgrade(upgrade::Version::V1)
|
|
|
|
.authenticate(
|
|
|
|
noise::NoiseConfig::xx(
|
|
|
|
noise::Keypair::<noise::X25519Spec>::new()
|
|
|
|
.into_authentic(&keypair)
|
|
|
|
.unwrap(),
|
|
|
|
)
|
|
|
|
.into_authenticated(),
|
|
|
|
)
|
|
|
|
.multiplex(yamux::YamuxConfig::default());
|
|
|
|
|
|
|
|
let transport = OrTransport::new(quic_transport, tcp_transport)
|
|
|
|
.map(|either_output, _| match either_output {
|
2023-01-23 23:31:30 +11:00
|
|
|
Either::Left((peer_id, muxer)) => (peer_id, StreamMuxerBox::new(muxer)),
|
|
|
|
Either::Right((peer_id, muxer)) => (peer_id, StreamMuxerBox::new(muxer)),
|
2022-11-14 16:12:16 +04:00
|
|
|
})
|
|
|
|
.boxed();
|
|
|
|
|
|
|
|
(peer_id, transport)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "async-std")]
|
|
|
|
#[async_std::test]
|
|
|
|
async fn tcp_and_quic() {
|
|
|
|
let (a_peer_id, mut a_transport) = new_tcp_quic_transport();
|
|
|
|
let (b_peer_id, mut b_transport) = new_tcp_quic_transport();
|
|
|
|
|
|
|
|
let quic_addr = start_listening(&mut a_transport, "/ip4/127.0.0.1/udp/0/quic-v1").await;
|
|
|
|
let tcp_addr = start_listening(&mut a_transport, "/ip4/127.0.0.1/tcp/0").await;
|
|
|
|
|
|
|
|
let ((a_connected, _, _), (b_connected, _)) =
|
|
|
|
connect(&mut a_transport, &mut b_transport, quic_addr).await;
|
|
|
|
assert_eq!(a_connected, b_peer_id);
|
|
|
|
assert_eq!(b_connected, a_peer_id);
|
|
|
|
|
|
|
|
let ((a_connected, _, _), (b_connected, _)) =
|
|
|
|
connect(&mut a_transport, &mut b_transport, tcp_addr).await;
|
|
|
|
assert_eq!(a_connected, b_peer_id);
|
|
|
|
assert_eq!(b_connected, a_peer_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note: This test should likely be ported to the muxer compliance test suite.
|
|
|
|
#[cfg(feature = "async-std")]
|
|
|
|
#[test]
|
|
|
|
fn concurrent_connections_and_streams_async_std() {
|
|
|
|
let _ = env_logger::try_init();
|
|
|
|
|
|
|
|
quickcheck::QuickCheck::new()
|
|
|
|
.min_tests_passed(1)
|
|
|
|
.quickcheck(prop::<quic::async_std::Provider> as fn(_, _) -> _);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note: This test should likely be ported to the muxer compliance test suite.
|
|
|
|
#[cfg(feature = "tokio")]
|
|
|
|
#[test]
|
|
|
|
fn concurrent_connections_and_streams_tokio() {
|
|
|
|
let _ = env_logger::try_init();
|
|
|
|
|
|
|
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
|
|
|
let _guard = rt.enter();
|
|
|
|
quickcheck::QuickCheck::new()
|
|
|
|
.min_tests_passed(1)
|
|
|
|
.quickcheck(prop::<quic::tokio::Provider> as fn(_, _) -> _);
|
|
|
|
}
|
|
|
|
|
2022-12-02 20:43:04 +01:00
|
|
|
#[cfg(feature = "tokio")]
|
|
|
|
#[tokio::test]
|
|
|
|
async fn draft_29_support() {
|
|
|
|
use std::task::Poll;
|
|
|
|
|
|
|
|
use futures::{future::poll_fn, select};
|
2022-12-13 07:58:01 +11:00
|
|
|
use libp2p_core::transport::TransportError;
|
2022-12-02 20:43:04 +01:00
|
|
|
|
|
|
|
let _ = env_logger::try_init();
|
|
|
|
|
|
|
|
let (_, mut a_transport) =
|
|
|
|
create_transport::<quic::tokio::Provider>(|cfg| cfg.support_draft_29 = true);
|
|
|
|
let (_, mut b_transport) =
|
|
|
|
create_transport::<quic::tokio::Provider>(|cfg| cfg.support_draft_29 = true);
|
|
|
|
|
|
|
|
// If a server supports draft-29 all its QUIC addresses can be dialed on draft-29 or version-1
|
|
|
|
let a_quic_addr = start_listening(&mut a_transport, "/ip4/127.0.0.1/udp/0/quic").await;
|
|
|
|
let a_quic_mapped_addr = swap_protocol!(a_quic_addr, Quic => QuicV1);
|
|
|
|
let a_quic_v1_addr = start_listening(&mut a_transport, "/ip4/127.0.0.1/udp/0/quic-v1").await;
|
|
|
|
let a_quic_v1_mapped_addr = swap_protocol!(a_quic_v1_addr, QuicV1 => Quic);
|
|
|
|
|
|
|
|
connect(&mut a_transport, &mut b_transport, a_quic_addr.clone()).await;
|
|
|
|
connect(&mut a_transport, &mut b_transport, a_quic_mapped_addr).await;
|
|
|
|
connect(&mut a_transport, &mut b_transport, a_quic_v1_addr).await;
|
|
|
|
connect(&mut a_transport, &mut b_transport, a_quic_v1_mapped_addr).await;
|
|
|
|
|
|
|
|
let (_, mut c_transport) =
|
|
|
|
create_transport::<quic::tokio::Provider>(|cfg| cfg.support_draft_29 = false);
|
|
|
|
assert!(matches!(
|
|
|
|
c_transport.dial(a_quic_addr),
|
|
|
|
Err(TransportError::MultiaddrNotSupported(_))
|
|
|
|
));
|
|
|
|
|
|
|
|
// Test disabling draft-29 on a server.
|
|
|
|
let (_, mut d_transport) =
|
|
|
|
create_transport::<quic::tokio::Provider>(|cfg| cfg.support_draft_29 = false);
|
|
|
|
assert!(matches!(
|
|
|
|
d_transport.listen_on("/ip4/127.0.0.1/udp/0/quic".parse().unwrap()),
|
|
|
|
Err(TransportError::MultiaddrNotSupported(_))
|
|
|
|
));
|
|
|
|
let d_quic_v1_addr = start_listening(&mut d_transport, "/ip4/127.0.0.1/udp/0/quic-v1").await;
|
|
|
|
let d_quic_addr_mapped = swap_protocol!(d_quic_v1_addr, QuicV1 => Quic);
|
|
|
|
let dial = b_transport.dial(d_quic_addr_mapped).unwrap();
|
|
|
|
let drive_transports = poll_fn::<(), _>(|cx| {
|
|
|
|
let _ = b_transport.poll_next_unpin(cx);
|
|
|
|
let _ = d_transport.poll_next_unpin(cx);
|
|
|
|
Poll::Pending
|
|
|
|
});
|
|
|
|
select! {
|
|
|
|
_ = drive_transports.fuse() => {}
|
|
|
|
result = dial.fuse() => {
|
|
|
|
#[allow(clippy::single_match)]
|
|
|
|
match result {
|
|
|
|
Ok(_) => panic!("Unexpected success dialing version-1-only server with draft-29."),
|
|
|
|
// FIXME: We currently get a Handshake timeout if the server does not support our version.
|
|
|
|
// Correct would be to get an quinn error "VersionMismatch".
|
|
|
|
Err(_) => {}
|
|
|
|
// Err(e) => assert!(format!("{:?}", e).contains("VersionMismatch"), "Got unexpected error {}", e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-25 10:05:50 +01:00
|
|
|
#[cfg(feature = "async-std")]
|
|
|
|
#[async_std::test]
|
|
|
|
async fn backpressure() {
|
|
|
|
let _ = env_logger::try_init();
|
|
|
|
let max_stream_data = quic::Config::new(&generate_tls_keypair()).max_stream_data;
|
|
|
|
|
|
|
|
let (mut stream_a, mut stream_b) = build_streams::<quic::async_std::Provider>().await;
|
|
|
|
|
|
|
|
let data = vec![0; max_stream_data as usize - 1];
|
|
|
|
|
|
|
|
stream_a.write_all(&data).await.unwrap();
|
|
|
|
|
|
|
|
let more_data = vec![0; 1];
|
|
|
|
assert!(stream_a.write(&more_data).now_or_never().is_none());
|
|
|
|
|
|
|
|
let mut buf = vec![1; max_stream_data as usize - 1];
|
|
|
|
stream_b.read_exact(&mut buf).await.unwrap();
|
|
|
|
|
|
|
|
let mut buf = [0];
|
|
|
|
assert!(stream_b.read(&mut buf).now_or_never().is_none());
|
|
|
|
|
|
|
|
assert!(stream_a.write(&more_data).now_or_never().is_some());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "async-std")]
|
|
|
|
#[async_std::test]
|
|
|
|
async fn read_after_peer_dropped_stream() {
|
|
|
|
let _ = env_logger::try_init();
|
|
|
|
let (mut stream_a, mut stream_b) = build_streams::<quic::async_std::Provider>().await;
|
|
|
|
|
|
|
|
let data = vec![0; 10];
|
|
|
|
|
|
|
|
stream_b.close().now_or_never();
|
|
|
|
stream_a.write_all(&data).await.unwrap();
|
|
|
|
stream_a.close().await.unwrap();
|
|
|
|
drop(stream_a);
|
|
|
|
|
|
|
|
stream_b.close().await.unwrap();
|
|
|
|
let mut buf = Vec::new();
|
|
|
|
stream_b.read_to_end(&mut buf).await.unwrap();
|
|
|
|
assert_eq!(data, buf)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "async-std")]
|
|
|
|
#[async_std::test]
|
|
|
|
#[should_panic]
|
|
|
|
async fn write_after_peer_dropped_stream() {
|
|
|
|
let _ = env_logger::try_init();
|
|
|
|
let (stream_a, mut stream_b) = build_streams::<quic::async_std::Provider>().await;
|
|
|
|
drop(stream_a);
|
|
|
|
futures_timer::Delay::new(Duration::from_millis(1)).await;
|
|
|
|
|
|
|
|
let data = vec![0; 10];
|
|
|
|
stream_b.write_all(&data).await.expect("Write failed.");
|
|
|
|
stream_b.close().await.expect("Close failed.");
|
|
|
|
}
|
|
|
|
|
2022-11-14 16:12:16 +04:00
|
|
|
async fn smoke<P: Provider>() {
|
|
|
|
let _ = env_logger::try_init();
|
|
|
|
|
2022-12-02 20:43:04 +01:00
|
|
|
let (a_peer_id, mut a_transport) = create_default_transport::<P>();
|
|
|
|
let (b_peer_id, mut b_transport) = create_default_transport::<P>();
|
2022-11-14 16:12:16 +04:00
|
|
|
|
|
|
|
let addr = start_listening(&mut a_transport, "/ip4/127.0.0.1/udp/0/quic-v1").await;
|
|
|
|
let ((a_connected, _, _), (b_connected, _)) =
|
|
|
|
connect(&mut a_transport, &mut b_transport, addr).await;
|
|
|
|
|
|
|
|
assert_eq!(a_connected, b_peer_id);
|
|
|
|
assert_eq!(b_connected, a_peer_id);
|
|
|
|
}
|
|
|
|
|
2022-11-25 10:05:50 +01:00
|
|
|
async fn build_streams<P: Provider>() -> (SubstreamBox, SubstreamBox) {
|
2022-12-02 20:43:04 +01:00
|
|
|
let (_, mut a_transport) = create_default_transport::<P>();
|
|
|
|
let (_, mut b_transport) = create_default_transport::<P>();
|
2022-11-25 10:05:50 +01:00
|
|
|
|
|
|
|
let addr = start_listening(&mut a_transport, "/ip4/127.0.0.1/udp/0/quic-v1").await;
|
|
|
|
let ((_, _, mut conn_a), (_, mut conn_b)) =
|
|
|
|
connect(&mut a_transport, &mut b_transport, addr).await;
|
|
|
|
let (stream_a_tx, stream_a_rx) = oneshot::channel();
|
|
|
|
let (stream_b_tx, stream_b_rx) = oneshot::channel();
|
|
|
|
|
|
|
|
P::spawn(async move {
|
|
|
|
let mut stream_a_tx = Some(stream_a_tx);
|
|
|
|
let mut stream_b_tx = Some(stream_b_tx);
|
|
|
|
poll_fn::<(), _>(move |cx| {
|
|
|
|
let _ = a_transport.poll_next_unpin(cx);
|
|
|
|
let _ = conn_a.poll_unpin(cx);
|
|
|
|
let _ = b_transport.poll_next_unpin(cx);
|
|
|
|
let _ = conn_b.poll_unpin(cx);
|
|
|
|
if stream_a_tx.is_some() {
|
|
|
|
if let Poll::Ready(stream) = conn_a.poll_outbound_unpin(cx) {
|
|
|
|
let tx = stream_a_tx.take().unwrap();
|
|
|
|
tx.send(stream.unwrap()).unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if stream_b_tx.is_some() {
|
|
|
|
if let Poll::Ready(stream) = conn_b.poll_inbound_unpin(cx) {
|
|
|
|
let tx = stream_b_tx.take().unwrap();
|
|
|
|
tx.send(stream.unwrap()).unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Poll::Pending
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
});
|
|
|
|
let mut stream_a = stream_a_rx.map(Result::unwrap).await;
|
|
|
|
|
|
|
|
// Send dummy byte to notify the peer of the new stream.
|
|
|
|
let send_buf = [0];
|
|
|
|
stream_a.write_all(&send_buf).await.unwrap();
|
|
|
|
|
|
|
|
let mut stream_b = stream_b_rx.map(Result::unwrap).await;
|
|
|
|
let mut recv_buf = [1];
|
|
|
|
|
|
|
|
assert_eq!(stream_b.read(&mut recv_buf).await.unwrap(), 1);
|
|
|
|
assert_eq!(send_buf, recv_buf);
|
|
|
|
|
|
|
|
(stream_a, stream_b)
|
|
|
|
}
|
|
|
|
|
2022-12-02 20:43:04 +01:00
|
|
|
#[macro_export]
|
|
|
|
macro_rules! swap_protocol {
|
|
|
|
($addr:expr, $From:ident => $To:ident) => {
|
|
|
|
$addr
|
|
|
|
.into_iter()
|
|
|
|
.map(|p| match p {
|
|
|
|
Protocol::$From => Protocol::$To,
|
|
|
|
_ => p,
|
|
|
|
})
|
|
|
|
.collect::<Multiaddr>()
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-12-13 07:58:01 +11:00
|
|
|
fn generate_tls_keypair() -> libp2p_core::identity::Keypair {
|
|
|
|
libp2p_core::identity::Keypair::generate_ed25519()
|
2022-11-14 16:12:16 +04:00
|
|
|
}
|
|
|
|
|
2022-12-02 20:43:04 +01:00
|
|
|
fn create_default_transport<P: Provider>() -> (PeerId, Boxed<(PeerId, StreamMuxerBox)>) {
|
|
|
|
create_transport::<P>(|_| {})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_transport<P: Provider>(
|
|
|
|
with_config: impl Fn(&mut quic::Config),
|
|
|
|
) -> (PeerId, Boxed<(PeerId, StreamMuxerBox)>) {
|
2022-11-14 16:12:16 +04:00
|
|
|
let keypair = generate_tls_keypair();
|
|
|
|
let peer_id = keypair.public().to_peer_id();
|
2022-12-02 20:43:04 +01:00
|
|
|
let mut config = quic::Config::new(&keypair);
|
|
|
|
with_config(&mut config);
|
|
|
|
let transport = quic::GenTransport::<P>::new(config)
|
2022-11-14 16:12:16 +04:00
|
|
|
.map(|(p, c), _| (p, StreamMuxerBox::new(c)))
|
|
|
|
.boxed();
|
|
|
|
|
|
|
|
(peer_id, transport)
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn start_listening(transport: &mut Boxed<(PeerId, StreamMuxerBox)>, addr: &str) -> Multiaddr {
|
|
|
|
transport.listen_on(addr.parse().unwrap()).unwrap();
|
|
|
|
match transport.next().await {
|
|
|
|
Some(TransportEvent::NewAddress { listen_addr, .. }) => listen_addr,
|
2022-12-14 16:45:04 +01:00
|
|
|
e => panic!("{e:?}"),
|
2022-11-14 16:12:16 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn prop<P: Provider + BlockOn>(
|
|
|
|
number_listeners: NonZeroU8,
|
|
|
|
number_streams: NonZeroU8,
|
|
|
|
) -> quickcheck::TestResult {
|
|
|
|
const BUFFER_SIZE: usize = 4096 * 10;
|
|
|
|
|
|
|
|
let number_listeners = u8::from(number_listeners) as usize;
|
|
|
|
let number_streams = u8::from(number_streams) as usize;
|
|
|
|
|
|
|
|
if number_listeners > 10 || number_streams > 10 {
|
|
|
|
return quickcheck::TestResult::discard();
|
|
|
|
}
|
|
|
|
|
|
|
|
let (listeners_tx, mut listeners_rx) = mpsc::channel(number_listeners);
|
|
|
|
|
|
|
|
log::info!("Creating {number_streams} streams on {number_listeners} connections");
|
|
|
|
|
|
|
|
// Spawn the listener nodes.
|
|
|
|
for _ in 0..number_listeners {
|
|
|
|
P::spawn({
|
|
|
|
let mut listeners_tx = listeners_tx.clone();
|
|
|
|
|
|
|
|
async move {
|
2022-12-02 20:43:04 +01:00
|
|
|
let (peer_id, mut listener) = create_default_transport::<P>();
|
2022-11-14 16:12:16 +04:00
|
|
|
let addr = start_listening(&mut listener, "/ip4/127.0.0.1/udp/0/quic-v1").await;
|
|
|
|
|
|
|
|
listeners_tx.send((peer_id, addr)).await.unwrap();
|
|
|
|
|
|
|
|
loop {
|
|
|
|
if let TransportEvent::Incoming { upgrade, .. } =
|
|
|
|
listener.select_next_some().await
|
|
|
|
{
|
|
|
|
let (_, connection) = upgrade.await.unwrap();
|
|
|
|
|
|
|
|
P::spawn(answer_inbound_streams::<P, BUFFER_SIZE>(connection));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
let (completed_streams_tx, completed_streams_rx) =
|
|
|
|
mpsc::channel(number_streams * number_listeners);
|
|
|
|
|
|
|
|
// For each listener node start `number_streams` requests.
|
|
|
|
P::spawn(async move {
|
2022-12-02 20:43:04 +01:00
|
|
|
let (_, mut dialer) = create_default_transport::<P>();
|
2022-11-14 16:12:16 +04:00
|
|
|
|
|
|
|
while let Some((_, listener_addr)) = listeners_rx.next().await {
|
|
|
|
let (_, connection) = dial(&mut dialer, listener_addr.clone()).await.unwrap();
|
|
|
|
|
|
|
|
P::spawn(open_outbound_streams::<P, BUFFER_SIZE>(
|
|
|
|
connection,
|
|
|
|
number_streams,
|
|
|
|
completed_streams_tx.clone(),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Drive the dialer.
|
|
|
|
loop {
|
|
|
|
dialer.next().await;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let completed_streams = number_streams * number_listeners;
|
|
|
|
|
|
|
|
// Wait for all streams to complete.
|
|
|
|
P::block_on(
|
|
|
|
completed_streams_rx
|
2022-11-18 22:04:16 +11:00
|
|
|
.take(completed_streams)
|
2022-11-14 16:12:16 +04:00
|
|
|
.collect::<Vec<_>>(),
|
|
|
|
Duration::from_secs(30),
|
|
|
|
);
|
|
|
|
|
|
|
|
quickcheck::TestResult::passed()
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn answer_inbound_streams<P: Provider, const BUFFER_SIZE: usize>(
|
|
|
|
mut connection: StreamMuxerBox,
|
|
|
|
) {
|
|
|
|
loop {
|
|
|
|
let mut inbound_stream = match future::poll_fn(|cx| {
|
|
|
|
let _ = connection.poll_unpin(cx)?;
|
|
|
|
|
|
|
|
connection.poll_inbound_unpin(cx)
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
{
|
|
|
|
Ok(s) => s,
|
|
|
|
Err(_) => return,
|
|
|
|
};
|
|
|
|
|
|
|
|
P::spawn(async move {
|
|
|
|
// FIXME: Need to write _some_ data before we can read on both sides.
|
|
|
|
// Do a ping-pong exchange.
|
|
|
|
{
|
|
|
|
let mut pong = [0u8; 4];
|
|
|
|
inbound_stream.write_all(b"PING").await.unwrap();
|
|
|
|
inbound_stream.flush().await.unwrap();
|
|
|
|
inbound_stream.read_exact(&mut pong).await.unwrap();
|
|
|
|
assert_eq!(&pong, b"PONG");
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut data = vec![0; BUFFER_SIZE];
|
|
|
|
|
|
|
|
inbound_stream.read_exact(&mut data).await.unwrap();
|
|
|
|
inbound_stream.write_all(&data).await.unwrap();
|
|
|
|
inbound_stream.close().await.unwrap();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn open_outbound_streams<P: Provider, const BUFFER_SIZE: usize>(
|
|
|
|
mut connection: StreamMuxerBox,
|
|
|
|
number_streams: usize,
|
|
|
|
completed_streams_tx: mpsc::Sender<()>,
|
|
|
|
) {
|
|
|
|
for _ in 0..number_streams {
|
|
|
|
let mut outbound_stream = future::poll_fn(|cx| {
|
|
|
|
let _ = connection.poll_unpin(cx)?;
|
|
|
|
|
|
|
|
connection.poll_outbound_unpin(cx)
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
P::spawn({
|
|
|
|
let mut completed_streams_tx = completed_streams_tx.clone();
|
|
|
|
|
|
|
|
async move {
|
|
|
|
// FIXME: Need to write _some_ data before we can read on both sides.
|
|
|
|
// Do a ping-pong exchange.
|
|
|
|
{
|
|
|
|
let mut ping = [0u8; 4];
|
|
|
|
outbound_stream.write_all(b"PONG").await.unwrap();
|
|
|
|
outbound_stream.flush().await.unwrap();
|
|
|
|
outbound_stream.read_exact(&mut ping).await.unwrap();
|
|
|
|
assert_eq!(&ping, b"PING");
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut data = vec![0; BUFFER_SIZE];
|
|
|
|
rand::thread_rng().fill_bytes(&mut data);
|
|
|
|
|
|
|
|
let mut received = Vec::new();
|
|
|
|
|
|
|
|
outbound_stream.write_all(&data).await.unwrap();
|
|
|
|
outbound_stream.flush().await.unwrap();
|
|
|
|
outbound_stream.read_to_end(&mut received).await.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(received, data);
|
|
|
|
|
|
|
|
completed_streams_tx.send(()).await.unwrap();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
log::info!("Created {number_streams} streams");
|
|
|
|
|
|
|
|
while future::poll_fn(|cx| connection.poll_unpin(cx))
|
|
|
|
.await
|
|
|
|
.is_ok()
|
|
|
|
{}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Helper function for driving two transports until they established a connection.
|
|
|
|
async fn connect(
|
|
|
|
listener: &mut Boxed<(PeerId, StreamMuxerBox)>,
|
|
|
|
dialer: &mut Boxed<(PeerId, StreamMuxerBox)>,
|
|
|
|
addr: Multiaddr,
|
|
|
|
) -> (
|
|
|
|
(PeerId, Multiaddr, StreamMuxerBox),
|
|
|
|
(PeerId, StreamMuxerBox),
|
|
|
|
) {
|
|
|
|
future::join(
|
|
|
|
async {
|
|
|
|
let (upgrade, send_back_addr) =
|
|
|
|
listener.select_next_some().await.into_incoming().unwrap();
|
|
|
|
let (peer_id, connection) = upgrade.await.unwrap();
|
|
|
|
|
|
|
|
(peer_id, send_back_addr, connection)
|
|
|
|
},
|
|
|
|
async { dial(dialer, addr).await.unwrap() },
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Helper function for dialling that also polls the `Transport`.
|
|
|
|
async fn dial(
|
|
|
|
transport: &mut Boxed<(PeerId, StreamMuxerBox)>,
|
|
|
|
addr: Multiaddr,
|
|
|
|
) -> io::Result<(PeerId, StreamMuxerBox)> {
|
|
|
|
match future::select(transport.dial(addr).unwrap(), transport.next()).await {
|
|
|
|
Either::Left((conn, _)) => conn,
|
|
|
|
Either::Right((event, _)) => {
|
|
|
|
panic!("Unexpected event: {event:?}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
trait BlockOn {
|
|
|
|
fn block_on<R>(future: impl Future<Output = R> + Send, timeout: Duration) -> R;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "async-std")]
|
|
|
|
impl BlockOn for libp2p_quic::async_std::Provider {
|
|
|
|
fn block_on<R>(future: impl Future<Output = R> + Send, timeout: Duration) -> R {
|
|
|
|
async_std::task::block_on(async_std::future::timeout(timeout, future)).unwrap()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "tokio")]
|
|
|
|
impl BlockOn for libp2p_quic::tokio::Provider {
|
|
|
|
fn block_on<R>(future: impl Future<Output = R> + Send, timeout: Duration) -> R {
|
|
|
|
tokio::runtime::Handle::current()
|
|
|
|
.block_on(tokio::time::timeout(timeout, future))
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
}
|