This repository has been archived by the owner on Jun 21, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement StreamMuxer trait for Connection (#2)
* start impl StreamMuxer trait * implement poll_outbound fn * implement poll fns * impl close and flush_all fns * wrap fields in arc and clone it in open_outbound Fixes: ``` error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement ``` * implement poll_event fn * add some comments * close `incoming_data_channels_rx` in close to prevent adding new data channels * add comment to destroy_outbound * drop locks early * reduce the number of locks * bring back noise and start smoke tests * reserve 1 for the initial substream * smoke tests: add webrtc fingerprint to dialing addr * use tokio executor in swarm * do not assume fingerprint to be last actually it's supposed to be 3rd (ip, port, fingerprint, peer_id) but let's not assume anything * increase read buf size to 8192 and reduce one in smoke tests * verify peer_id and close initial stream (used for noise) * allow changing read buf cap in Connection * use Default::default when constructing RTCDataChannelInit * remove unnecessary field from RTCDataChannelInit * remove next_outbound_channel_id It turned out we actually don't need it. webrtc-rs lib will increase channel ID for us (on both ends on the connection). Also, extract `register_data_channel_open_handler` fn to reduce the amount of duplicated code. * fix some clippy warnings * exchange TLS fingerprint certificates inside noise Fixes #2 (comment) * remove udp_mux files and update PollDataChannel based on new ice, sctp and data crates * include old code
Showing
11 changed files
with
1,442 additions
and
1,059 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,305 @@ | ||
use anyhow::Result; | ||
use async_trait::async_trait; | ||
use futures::future::FutureExt; | ||
use futures::io::{AsyncRead, AsyncWrite, AsyncWriteExt}; | ||
use futures::stream::StreamExt; | ||
use libp2p_core::identity; | ||
use libp2p_core::multiaddr::Protocol; | ||
use libp2p_core::upgrade; | ||
use libp2p_request_response::{ | ||
ProtocolName, ProtocolSupport, RequestResponse, RequestResponseCodec, RequestResponseConfig, | ||
RequestResponseEvent, RequestResponseMessage, | ||
}; | ||
use libp2p_swarm::{Swarm, SwarmBuilder, SwarmEvent}; | ||
use libp2p_webrtc_direct::transport::WebRTCDirectTransport; | ||
use log::trace; | ||
use rand::RngCore; | ||
use rcgen::KeyPair; | ||
use tokio_crate as tokio; | ||
use webrtc::peer_connection::certificate::RTCCertificate; | ||
|
||
use std::borrow::Cow; | ||
use std::{io, iter}; | ||
|
||
fn generate_certificate() -> RTCCertificate { | ||
let kp = KeyPair::generate(&rcgen::PKCS_ECDSA_P256_SHA256).expect("key pair"); | ||
RTCCertificate::from_key_pair(kp).expect("certificate") | ||
} | ||
|
||
fn generate_tls_keypair() -> identity::Keypair { | ||
identity::Keypair::generate_ed25519() | ||
} | ||
|
||
async fn create_swarm() -> Result<(Swarm<RequestResponse<PingCodec>>, String)> { | ||
let cert = generate_certificate(); | ||
let keypair = generate_tls_keypair(); | ||
let peer_id = keypair.public().to_peer_id(); | ||
let transport = WebRTCDirectTransport::new(cert, keypair, "127.0.0.1:0").await?; | ||
let fingerprint = transport.cert_fingerprint(); | ||
let protocols = iter::once((PingProtocol(), ProtocolSupport::Full)); | ||
let cfg = RequestResponseConfig::default(); | ||
let behaviour = RequestResponse::new(PingCodec(), protocols, cfg); | ||
trace!("{}", peer_id); | ||
Ok(( | ||
SwarmBuilder::new(transport.boxed(), behaviour, peer_id) | ||
.executor(Box::new(|fut| { | ||
tokio::spawn(fut); | ||
})) | ||
.build(), | ||
fingerprint, | ||
)) | ||
} | ||
|
||
#[tokio::test] | ||
async fn smoke() -> Result<()> { | ||
let _ = env_logger::builder().is_test(true).try_init(); | ||
|
||
let mut rng = rand::thread_rng(); | ||
|
||
let (mut a, a_fingerprint) = create_swarm().await?; | ||
let (mut b, _b_fingerprint) = create_swarm().await?; | ||
|
||
Swarm::listen_on(&mut a, "/ip4/127.0.0.1/udp/0".parse()?)?; | ||
|
||
let addr = match a.next().await { | ||
Some(SwarmEvent::NewListenAddr { address, .. }) => address, | ||
e => panic!("{:?}", e), | ||
}; | ||
let addr = addr.with(Protocol::XWebRTC(hex_to_cow( | ||
&a_fingerprint.replace(":", ""), | ||
))); | ||
|
||
let mut data = vec![0; 4096]; | ||
rng.fill_bytes(&mut data); | ||
|
||
b.behaviour_mut() | ||
.add_address(&Swarm::local_peer_id(&a), addr); | ||
b.behaviour_mut() | ||
.send_request(&Swarm::local_peer_id(&a), Ping(data.clone())); | ||
|
||
match b.next().await { | ||
Some(SwarmEvent::Dialing(_)) => {}, | ||
e => panic!("{:?}", e), | ||
} | ||
|
||
match a.next().await { | ||
Some(SwarmEvent::IncomingConnection { .. }) => {}, | ||
e => panic!("{:?}", e), | ||
}; | ||
|
||
match b.next().await { | ||
Some(SwarmEvent::ConnectionEstablished { .. }) => {}, | ||
e => panic!("{:?}", e), | ||
}; | ||
|
||
match a.next().await { | ||
Some(SwarmEvent::ConnectionEstablished { .. }) => {}, | ||
e => panic!("{:?}", e), | ||
}; | ||
|
||
assert!(b.next().now_or_never().is_none()); | ||
|
||
match a.next().await { | ||
Some(SwarmEvent::Behaviour(RequestResponseEvent::Message { | ||
message: | ||
RequestResponseMessage::Request { | ||
request: Ping(ping), | ||
channel, | ||
.. | ||
}, | ||
.. | ||
})) => { | ||
a.behaviour_mut() | ||
.send_response(channel, Pong(ping)) | ||
.unwrap(); | ||
}, | ||
e => panic!("{:?}", e), | ||
} | ||
|
||
match a.next().await { | ||
Some(SwarmEvent::Behaviour(RequestResponseEvent::ResponseSent { .. })) => {}, | ||
e => panic!("{:?}", e), | ||
} | ||
|
||
match b.next().await { | ||
Some(SwarmEvent::Behaviour(RequestResponseEvent::Message { | ||
message: | ||
RequestResponseMessage::Response { | ||
response: Pong(pong), | ||
.. | ||
}, | ||
.. | ||
})) => assert_eq!(data, pong), | ||
e => panic!("{:?}", e), | ||
} | ||
|
||
a.behaviour_mut().send_request( | ||
&Swarm::local_peer_id(&b), | ||
Ping(b"another substream".to_vec()), | ||
); | ||
|
||
assert!(a.next().now_or_never().is_none()); | ||
|
||
match b.next().await { | ||
Some(SwarmEvent::Behaviour(RequestResponseEvent::Message { | ||
message: | ||
RequestResponseMessage::Request { | ||
request: Ping(data), | ||
channel, | ||
.. | ||
}, | ||
.. | ||
})) => { | ||
b.behaviour_mut() | ||
.send_response(channel, Pong(data)) | ||
.unwrap(); | ||
}, | ||
e => panic!("{:?}", e), | ||
} | ||
|
||
match b.next().await { | ||
Some(SwarmEvent::Behaviour(RequestResponseEvent::ResponseSent { .. })) => {}, | ||
e => panic!("{:?}", e), | ||
} | ||
|
||
match a.next().await { | ||
Some(SwarmEvent::Behaviour(RequestResponseEvent::Message { | ||
message: | ||
RequestResponseMessage::Response { | ||
response: Pong(data), | ||
.. | ||
}, | ||
.. | ||
})) => assert_eq!(data, b"another substream".to_vec()), | ||
e => panic!("{:?}", e), | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
struct PingProtocol(); | ||
|
||
#[derive(Clone)] | ||
struct PingCodec(); | ||
|
||
#[derive(Debug, Clone, PartialEq, Eq)] | ||
struct Ping(Vec<u8>); | ||
|
||
#[derive(Debug, Clone, PartialEq, Eq)] | ||
struct Pong(Vec<u8>); | ||
|
||
impl ProtocolName for PingProtocol { | ||
fn protocol_name(&self) -> &[u8] { | ||
"/ping/1".as_bytes() | ||
} | ||
} | ||
|
||
#[async_trait] | ||
impl RequestResponseCodec for PingCodec { | ||
type Protocol = PingProtocol; | ||
type Request = Ping; | ||
type Response = Pong; | ||
|
||
async fn read_request<T>(&mut self, _: &PingProtocol, io: &mut T) -> io::Result<Self::Request> | ||
where | ||
T: AsyncRead + Unpin + Send, | ||
{ | ||
upgrade::read_length_prefixed(io, 4096) | ||
.map(|res| match res { | ||
Err(e) => Err(io::Error::new(io::ErrorKind::InvalidData, e)), | ||
Ok(vec) if vec.is_empty() => Err(io::ErrorKind::UnexpectedEof.into()), | ||
Ok(vec) => Ok(Ping(vec)), | ||
}) | ||
.await | ||
} | ||
|
||
async fn read_response<T>(&mut self, _: &PingProtocol, io: &mut T) -> io::Result<Self::Response> | ||
where | ||
T: AsyncRead + Unpin + Send, | ||
{ | ||
upgrade::read_length_prefixed(io, 4096) | ||
.map(|res| match res { | ||
Err(e) => Err(io::Error::new(io::ErrorKind::InvalidData, e)), | ||
Ok(vec) if vec.is_empty() => Err(io::ErrorKind::UnexpectedEof.into()), | ||
Ok(vec) => Ok(Pong(vec)), | ||
}) | ||
.await | ||
} | ||
|
||
async fn write_request<T>( | ||
&mut self, | ||
_: &PingProtocol, | ||
io: &mut T, | ||
Ping(data): Ping, | ||
) -> io::Result<()> | ||
where | ||
T: AsyncWrite + Unpin + Send, | ||
{ | ||
upgrade::write_length_prefixed(io, data).await?; | ||
io.close().await?; | ||
Ok(()) | ||
} | ||
|
||
async fn write_response<T>( | ||
&mut self, | ||
_: &PingProtocol, | ||
io: &mut T, | ||
Pong(data): Pong, | ||
) -> io::Result<()> | ||
where | ||
T: AsyncWrite + Unpin + Send, | ||
{ | ||
upgrade::write_length_prefixed(io, data).await?; | ||
io.close().await?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
#[tokio::test] | ||
async fn dial_failure() -> Result<()> { | ||
let _ = env_logger::builder().is_test(true).try_init(); | ||
|
||
let (mut a, a_fingerprint) = create_swarm().await?; | ||
let (mut b, _b_fingerprint) = create_swarm().await?; | ||
|
||
Swarm::listen_on(&mut a, "/ip4/127.0.0.1/udp/0".parse()?)?; | ||
|
||
let addr = match a.next().await { | ||
Some(SwarmEvent::NewListenAddr { address, .. }) => address, | ||
e => panic!("{:?}", e), | ||
}; | ||
let addr = addr.with(Protocol::XWebRTC(hex_to_cow( | ||
&a_fingerprint.replace(":", ""), | ||
))); | ||
|
||
let a_peer_id = &Swarm::local_peer_id(&a).clone(); | ||
drop(a); // stop a swarm so b can never reach it | ||
|
||
b.behaviour_mut().add_address(a_peer_id, addr); | ||
b.behaviour_mut() | ||
.send_request(a_peer_id, Ping(b"hello world".to_vec())); | ||
|
||
match b.next().await { | ||
Some(SwarmEvent::Dialing(_)) => {}, | ||
e => panic!("{:?}", e), | ||
} | ||
|
||
match b.next().await { | ||
Some(SwarmEvent::OutgoingConnectionError { .. }) => {}, | ||
e => panic!("{:?}", e), | ||
}; | ||
|
||
match b.next().await { | ||
Some(SwarmEvent::Behaviour(RequestResponseEvent::OutboundFailure { .. })) => {}, | ||
e => panic!("{:?}", e), | ||
}; | ||
|
||
Ok(()) | ||
} | ||
|
||
fn hex_to_cow<'a>(s: &str) -> Cow<'a, [u8; 32]> { | ||
let mut buf = [0; 32]; | ||
hex::decode_to_slice(s, &mut buf).unwrap(); | ||
Cow::Owned(buf) | ||
} |