browser_webrtc_example/
lib.rs1#![cfg(target_arch = "wasm32")]
2
3use std::{io, time::Duration};
4
5use futures::StreamExt;
6use js_sys::Date;
7use libp2p::{core::Multiaddr, ping, swarm::SwarmEvent};
8use libp2p_webrtc_websys as webrtc_websys;
9use wasm_bindgen::prelude::*;
10use web_sys::{Document, HtmlElement};
11
12#[wasm_bindgen]
13pub async fn run(libp2p_endpoint: String) -> Result<(), JsError> {
14 tracing_wasm::set_as_global_default();
15
16 let ping_duration = Duration::from_secs(30);
17
18 let body = Body::from_current_window()?;
19 body.append_p(&format!(
20 "Let's ping the rust-libp2p server over WebRTC for {:?}:",
21 ping_duration
22 ))?;
23
24 let mut swarm = libp2p::SwarmBuilder::with_new_identity()
25 .with_wasm_bindgen()
26 .with_other_transport(|key| {
27 webrtc_websys::Transport::new(webrtc_websys::Config::new(&key))
28 })?
29 .with_behaviour(|_| ping::Behaviour::new(ping::Config::new()))?
30 .with_swarm_config(|c| c.with_idle_connection_timeout(ping_duration))
31 .build();
32
33 let addr = libp2p_endpoint.parse::<Multiaddr>()?;
34 tracing::info!("Dialing {addr}");
35 swarm.dial(addr)?;
36
37 loop {
38 match swarm.next().await.unwrap() {
39 SwarmEvent::Behaviour(ping::Event { result: Err(e), .. }) => {
40 tracing::error!("Ping failed: {:?}", e);
41
42 break;
43 }
44 SwarmEvent::Behaviour(ping::Event {
45 peer,
46 result: Ok(rtt),
47 ..
48 }) => {
49 tracing::info!("Ping successful: RTT: {rtt:?}, from {peer}");
50 body.append_p(&format!("RTT: {rtt:?} at {}", Date::new_0().to_string()))?;
51 }
52 SwarmEvent::ConnectionClosed {
53 cause: Some(cause), ..
54 } => {
55 tracing::info!("Swarm event: {:?}", cause);
56
57 if let libp2p::swarm::ConnectionError::KeepAliveTimeout = cause {
58 body.append_p("All done with pinging! ")?;
59
60 break;
61 }
62 body.append_p(&format!("Connection closed due to: {:?}", cause))?;
63 }
64 evt => tracing::info!("Swarm event: {:?}", evt),
65 }
66 }
67
68 Ok(())
69}
70
71struct Body {
73 body: HtmlElement,
74 document: Document,
75}
76
77impl Body {
78 fn from_current_window() -> Result<Self, JsError> {
79 let document = web_sys::window()
82 .ok_or(js_error("no global `window` exists"))?
83 .document()
84 .ok_or(js_error("should have a document on window"))?;
85 let body = document
86 .body()
87 .ok_or(js_error("document should have a body"))?;
88
89 Ok(Self { body, document })
90 }
91
92 fn append_p(&self, msg: &str) -> Result<(), JsError> {
93 let val = self
94 .document
95 .create_element("p")
96 .map_err(|_| js_error("failed to create <p>"))?;
97 val.set_text_content(Some(msg));
98 self.body
99 .append_child(&val)
100 .map_err(|_| js_error("failed to append <p>"))?;
101
102 Ok(())
103 }
104}
105
106fn js_error(msg: &str) -> JsError {
107 io::Error::new(io::ErrorKind::Other, msg).into()
108}