Skip to content

feat: Add WebSocket and WebRTC transports to relay server example #6030

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion examples/relay-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ release = false
clap = { version = "4.5.6", features = ["derive"] }
tokio = { version = "1.37.0", features = ["full"] }
futures = { workspace = true }
libp2p = { path = "../../libp2p", features = ["tokio", "noise", "macros", "ping", "tcp", "identify", "yamux", "relay", "quic"] }
libp2p = { path = "../../libp2p", features = ["tokio", "noise", "macros", "ping", "tcp", "identify", "yamux", "relay", "quic", "websocket", "dns", "tls"] }
libp2p-webrtc = { workspace = true, features = ["tokio"] }
libp2p-noise = { workspace = true }
libp2p-tls = { workspace = true }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use them through the libp2p meta crate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the tls & noise , but webrtc is not exposed via the libp2p meta crate

tracing-subscriber = { workspace = true, features = ["env-filter"] }
tracing = "0.1"
rand = "0.8"

[lints]
workspace = true
67 changes: 58 additions & 9 deletions examples/relay-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,70 @@ The **libp2p** relay example showcases how to create a relay node that can route

To run the example, follow these steps:

1. Run the relay node by executing the following command:
#### 1. Run the relay nodes:
##### Running with Cargo

```sh
cargo run -- --port <port> --secret-key-seed <seed>
```
You can start the relay node manually using Cargo and the available command-line arguments:

Replace `<port>` with the port number on which the relay node will listen for incoming connections.
Replace `<seed>` with a seed value used to generate a deterministic peer ID for the relay node.
```sh
cargo run -- --port <port> --secret-key-seed <seed> [--websocket-port <ws-port>] [--webrtc-port <wrtc-port>]
```

2. The relay node will start listening for incoming connections.
- Replace `<port>` with the port number on which the relay node will listen for incoming connections (TCP and QUIC).
- Replace `<seed>` with a seed value used to generate a deterministic peer ID for the relay node.
- Use `--websocket-port <ws-port>` to enable WebSocket support on the specified port.
- Use `--webrtc-port <wrtc-port>` to enable WebRTC support on the specified port.
- If you do **not** provide `--websocket-port` or `--webrtc-port`, the relay will **not** listen on those transports.

**Example:**
```sh
cargo run -- --port 9000 --secret-key-seed 42 --websocket-port 8080 --webrtc-port 8081
```
This will start the relay node with TCP and QUIC on port 9000, WebSocket on port 8080, and WebRTC on port 8081.

##### Using Provided Scripts

To simplify repeated tests and ensure deterministic peer IDs and multiaddresses, you can use the provided `run*.sh` scripts. These scripts automate the process of starting the relay server with predefined seeds and ports, making it easier to connect different clients for testing.

- **Run the relay server with only TCP and QUIC transports:**
```sh
./run-relay.sh
```
This script starts the relay server with a fixed secret key seed and port, but **only enables TCP and QUIC** transports (no WebSocket or WebRTC).

- **Run the relay server with WebRTC support:**
```sh
./run-relay-webrtc.sh
```
This script starts the relay server with WebRTC enabled on a specified port.

- **Run the relay server with WebSocket support:**
```sh
./run-relay-websocket.sh
```
This script starts the relay server with WebSocket enabled on a specified port.

- **Run the relay server with all supported transports (TCP, QUIC, WebRTC, and WebSocket):**
```sh
./run-relay-all.sh
```
This script starts the relay server with TCP, QUIC, WebSocket, and WebRTC enabled, using deterministic parameters for reproducible tests:
```sh
RUST_LOG=info cargo run -- --port 4884 --secret-key-seed 0 --websocket-port 8080 --webrtc-port 8081
```

**Note:**
If you do not provide a port for `--websocket-port` or `--webrtc-port`, the relay will not listen on those transports. Port `0` is not supported for WebSocket or WebRTC in this example; you must specify a fixed port number.

Check the script files for details on the specific seeds, ports, and options used. You can modify these scripts to match your testing requirements.


#### 2. The relay node will start listening for incoming connections.
It will print the listening address once it is ready.

3. Connect other **libp2p** nodes to the relay node by specifying the relay's listening address as one of the bootstrap nodes in their configuration.
#### 3. Connect other **libp2p** nodes to the relay node by specifying the relay's listening address as one of the bootstrap nodes in their configuration.

4. Once the connections are established, the relay node will facilitate communication between the connected peers, allowing them to exchange messages and data.
#### 4. Once the connections are established, the relay node will facilitate communication between the connected peers, allowing them to exchange messages and data.

## Conclusion

Expand Down
1 change: 1 addition & 0 deletions examples/relay-server/run-relay-all.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
RUST_LOG=info cargo run -- --port 4884 --secret-key-seed 0 --websocket-port 8080 --webrtc-port 8081
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate the effort, but I don't think we should add run scripts that hard-code the parameters. Most user want to change these parameters anyway, and having to think about them IMO also helps the general understanding.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I will move them to a new repo that provide examples.

1 change: 1 addition & 0 deletions examples/relay-server/run-relay-webrtc.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
RUST_LOG=info cargo run -- --port 4884 --secret-key-seed 0 --webrtc-port 8081
1 change: 1 addition & 0 deletions examples/relay-server/run-relay-websocket.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
RUST_LOG=info cargo run -- --port 4884 --secret-key-seed 0 --websocket-port 8080
1 change: 1 addition & 0 deletions examples/relay-server/run-relay.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
RUST_LOG=info cargo run -- --port 4884 --secret-key-seed 0
75 changes: 72 additions & 3 deletions examples/relay-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@ use std::{
use clap::Parser;
use futures::StreamExt;
use libp2p::{
core::{multiaddr::Protocol, Multiaddr},
core::{multiaddr::Protocol, muxing::StreamMuxerBox, Multiaddr, Transport},
identify, identity, noise, ping, relay,
swarm::{NetworkBehaviour, SwarmEvent},
tcp, yamux,
tcp, yamux, PeerId, Swarm,
};
use libp2p_webrtc as webrtc;
use rand::thread_rng;
use tracing::info;
use tracing_subscriber::EnvFilter;

#[tokio::main]
Expand All @@ -46,6 +49,8 @@ async fn main() -> Result<(), Box<dyn Error>> {

// Create a static known PeerId based on given secret
let local_key: identity::Keypair = generate_ed25519(opt.secret_key_seed);
let local_peer_id = PeerId::from(local_key.public());
info!(?local_peer_id, "Local peer id");

let mut swarm = libp2p::SwarmBuilder::with_existing_identity(local_key)
.with_tokio()
Expand All @@ -55,6 +60,16 @@ async fn main() -> Result<(), Box<dyn Error>> {
yamux::Config::default,
)?
.with_quic()
.with_other_transport(|id_keys| {
Ok(webrtc::tokio::Transport::new(
id_keys.clone(),
webrtc::tokio::Certificate::generate(&mut thread_rng())?,
)
.map(|(peer_id, conn), _| (peer_id, StreamMuxerBox::new(conn))))
})?
.with_dns()?
.with_websocket(noise::Config::new, yamux::Config::default)
.await?
.with_behaviour(|key| Behaviour {
relay: relay::Behaviour::new(key.public().to_peer_id(), Default::default()),
ping: ping::Behaviour::new(ping::Config::new()),
Expand Down Expand Up @@ -83,6 +98,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
.with(Protocol::QuicV1);
swarm.listen_on(listen_addr_quic)?;

listen_on_websocket(&mut swarm, &opt)?;
listen_on_webrtc(&mut swarm, &opt)?;

loop {
match swarm.next().await.expect("Infinite Stream.") {
SwarmEvent::Behaviour(event) => {
Expand All @@ -96,14 +114,57 @@ async fn main() -> Result<(), Box<dyn Error>> {

println!("{event:?}")
}
SwarmEvent::NewListenAddr { address, .. } => {
SwarmEvent::NewListenAddr { mut address, .. } => {
address.push(Protocol::P2p(local_peer_id));
println!("Listening on {address:?}");
}
_ => {}
}
}
}

fn listen_on_websocket(swarm: &mut Swarm<Behaviour>, opt: &Opt) -> Result<(), Box<dyn Error>> {
match opt.websocket_port {
Some(0) => {
return Err(Box::new(std::io::Error::new(std::io::ErrorKind::InvalidInput,
"Websocket port is 0, which is not supported in this example, since it will use a non-deterministic port. Please use a fixed port for websocket.")));
}
Some(port) => {
let address = Multiaddr::from(Ipv4Addr::UNSPECIFIED)
.with(Protocol::Tcp(port))
.with(Protocol::Ws(std::borrow::Cow::Borrowed("/")));

info!(?address, "Listening on webSocket");
swarm.listen_on(address.clone())?;
}
None => {
info!("Does not use websocket");
}
}
Ok(())
}

fn listen_on_webrtc(swarm: &mut Swarm<Behaviour>, opt: &Opt) -> Result<(), Box<dyn Error>> {
match opt.webrtc_port {
Some(0) => {
return Err(Box::new(std::io::Error::new(std::io::ErrorKind::InvalidInput,
"Websocket port is 0, which is not supported in this example, since it will use a non-deterministic port. Please use a fixed port for webRTC.")));
}
Some(port) => {
let address = Multiaddr::from(Ipv4Addr::UNSPECIFIED)
.with(Protocol::Udp(port))
.with(Protocol::WebRTCDirect);

info!(?address, "Listening on webRTC");
swarm.listen_on(address.clone())?;
}
None => {
info!("Does not use webRTC");
}
}
Ok(())
}

#[derive(NetworkBehaviour)]
struct Behaviour {
relay: relay::Behaviour,
Expand Down Expand Up @@ -132,4 +193,12 @@ struct Opt {
/// The port used to listen on all interfaces
#[arg(long)]
port: u16,

/// The websocket port used to listen on all interfaces
#[arg(long)]
websocket_port: Option<u16>,

/// The webrtc port used to listen on all interfaces
#[arg(long)]
webrtc_port: Option<u16>,
}
Loading