Implement RtcTransport (webrtc-rs) behind the Transport trait, so every native
platform that links tethercore gets direct P2P alongside SSE from one impl.
Signaling rides the existing server bus and matches the web client byte-for-
byte (presence chirp, lower-id-offers tie-break, {kind, sdp|candidate} signal
envelopes, "tether" data channel carrying raw clipboard text) — so Rust peers
interoperate with the browser's native RTCPeerConnection.
Dedup now keys on (from, text) with a 3s TTL so the SSE and data-channel copies
of the same clipboard collapse to one; the faster RTC copy wins.
Proven by examples/rtc_p2p.rs: two engines bring up a data channel and a
clipboard is delivered over it (received source == peer from-id, confirming the
RTC path beat the SSE relay).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
62 lines
2.3 KiB
Rust
62 lines
2.3 KiB
Rust
//! 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<Message>);
|
|
impl MessageHandler for Collector {
|
|
fn on_message(&self, msg: Message) {
|
|
let _ = self.0.send(msg);
|
|
}
|
|
fn on_status(&self, _connected: bool) {}
|
|
}
|
|
|
|
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());
|
|
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());
|
|
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();
|
|
}
|