//! Proves two engines establish a WebRTC data channel and clipboard flows P2P. //! cargo run --example rtc_p2p -- http://192.168.11.233:8765 //! //! Path discriminator: an RTC-delivered message has source == the peer's //! from-id; the SSE copy has source == the peer's source-label. Since RTC is //! direct (no server round-trip) it should win the dedup race, so a received //! source equal to the from-id proves delivery came over the data channel. use std::sync::mpsc; use std::time::Duration; use tethercore::{Engine, Message, MessageHandler}; struct Collector(mpsc::Sender); impl MessageHandler for Collector { fn on_message(&self, msg: Message) { let _ = self.0.send(msg); } fn on_status(&self, _connected: bool) {} fn on_file(&self, _name: String, _mime: String, _data: Vec) {} } fn main() { let server = std::env::args() .nth(1) .unwrap_or_else(|| "http://192.168.11.233:8765".into()); let room = "rtc-p2p-spike".to_string(); let (tx, rx) = mpsc::channel(); let a = Engine::new(server.clone(), room.clone(), "peer-a".into(), "label-a".into(), "Peer A".into(), "".into()); a.start(Box::new(Collector(tx))); // B's from-id and source-label are deliberately different so we can tell // which transport delivered. let b_from = "peer-b"; let b = Engine::new(server, room, b_from.into(), "label-b-sse".into(), "Peer B".into(), "".into()); b.start(Box::new(Collector(mpsc::channel().0))); // B needs to be live to negotiate // Give presence + ICE + DTLS time to bring the data channel up. eprintln!("waiting for RTC negotiation…"); std::thread::sleep(Duration::from_secs(6)); let payload = "hello-over-rtc"; b.send(payload.to_string()); match rx.recv_timeout(Duration::from_secs(5)) { Ok(m) if m.text == payload => { let via = if m.source == b_from { "RTC data channel ✅" } else { "SSE relay (RTC lost the race)" }; println!("received {:?}\n from={} source={} → delivered via {}", m.text, m.from, m.source, via); if m.source != b_from { println!(" note: message arrived, but over SSE — check the 'data channel open' log above to confirm RTC linked"); } } Ok(m) => println!("⚠️ unexpected: {m:?}"), Err(_) => { println!("❌ nothing received in time"); std::process::exit(1); } } a.stop(); b.stop(); }