2 Commits

Author SHA1 Message Date
Siavash Sameni
f843a934fe fix(relay): forward MediaPathReport across federation
MediaPathReport was only delivered via local signal_hub, so calls
between peers on different relays always hit peer_report_timeout
and fell back to relay — even when direct P2P worked perfectly.

Fix: check peer_relay_fp in call_registry (same pattern as
DirectCallAnswer). If the peer is on a remote relay, wrap in
FederatedSignalForward and send via federation link. Also fix
the cross-relay dispatcher to deliver to BOTH caller and callee
(not just caller), since the report can come from either side.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 14:14:30 +04:00
Siavash Sameni
b79073c649 Revert "fix(connect): trust direct path on peer report timeout"
This reverts commit 82b439595c.
2026-04-12 14:10:44 +04:00
2 changed files with 57 additions and 34 deletions

View File

@@ -688,18 +688,28 @@ async fn main() -> anyhow::Result<()> {
} }
// Phase 6: MediaPathReport forwarded across // Phase 6: MediaPathReport forwarded across
// federation — deliver to the local participant // federation — deliver to the LOCAL participant.
// of the matching call. // The report comes from the remote side, so we
// deliver to whichever participant is local. In
// the cross-relay case, one is local and one is
// remote. Try both — send_to is a no-op if the
// target isn't connected to this relay.
SignalMessage::MediaPathReport { ref call_id, .. } => { SignalMessage::MediaPathReport { ref call_id, .. } => {
// Deliver to the local caller (the cross-relay let (caller_fp, callee_fp) = {
// dispatcher only handles calls where the caller
// is local and the callee is remote, or vice versa)
let caller_fp = {
let reg = call_registry_d.lock().await; let reg = call_registry_d.lock().await;
reg.get(call_id).map(|c| c.caller_fingerprint.clone()) match reg.get(call_id) {
Some(c) => (
Some(c.caller_fingerprint.clone()),
Some(c.callee_fingerprint.clone()),
),
None => (None, None),
}
}; };
let hub = signal_hub_d.lock().await;
if let Some(fp) = caller_fp { if let Some(fp) = caller_fp {
let hub = signal_hub_d.lock().await; let _ = hub.send_to(&fp, &inner).await;
}
if let Some(fp) = callee_fp {
let _ = hub.send_to(&fp, &inner).await; let _ = hub.send_to(&fp, &inner).await;
} }
} }
@@ -1315,14 +1325,43 @@ async fn main() -> anyhow::Result<()> {
// call peer so both sides can negotiate // call peer so both sides can negotiate
// the media path before committing. // the media path before committing.
SignalMessage::MediaPathReport { ref call_id, .. } => { SignalMessage::MediaPathReport { ref call_id, .. } => {
let peer_fp = { // Look up peer AND check if this is a
// cross-relay call (same pattern as
// DirectCallAnswer).
let (peer_fp, peer_relay_fp) = {
let reg = call_registry.lock().await; let reg = call_registry.lock().await;
reg.peer_fingerprint(call_id, &client_fp) match reg.get(call_id) {
.map(|s| s.to_string()) Some(c) => (
reg.peer_fingerprint(call_id, &client_fp)
.map(|s| s.to_string()),
c.peer_relay_fp.clone(),
),
None => (None, None),
}
}; };
if let Some(fp) = peer_fp { if let Some(fp) = peer_fp {
let hub = signal_hub.lock().await; if let Some(ref origin_fp) = peer_relay_fp {
let _ = hub.send_to(&fp, &msg).await; // Cross-relay: wrap and forward
if let Some(ref fm) = federation_mgr {
let forward = SignalMessage::FederatedSignalForward {
inner: Box::new(msg.clone()),
origin_relay_fp: tls_fp.clone(),
};
if let Err(e) = fm.send_signal_to_peer(origin_fp, &forward).await {
warn!(
%call_id,
%origin_fp,
error = %e,
"cross-relay MediaPathReport forward failed"
);
}
}
} else {
// Local call
let hub = signal_hub.lock().await;
let _ = hub.send_to(&fp, &msg).await;
}
} }
} }

View File

@@ -503,29 +503,13 @@ async fn connect(
peer_ok peer_ok
} }
_ => { _ => {
// Timeout — the peer's report // Timeout or channel error — peer
// didn't arrive. This happens when // may be on an old build without
// peers are on different relays // Phase 6. Fall back to relay.
// (no federation for MediaPathReport) emit_call_debug(&app, "connect:peer_report_timeout", serde_json::json!({}));
// or the peer is on an old build.
//
// If OUR direct path succeeded and
// the transport is still alive,
// trust it — the timeout is a relay
// forwarding issue, not a direct
// path failure.
let mut sig = state.signal.lock().await; let mut sig = state.signal.lock().await;
sig.pending_path_report = None; sig.pending_path_report = None;
let trust_direct = local_direct_ok false
&& race_result.direct_transport.as_ref()
.map(|t| t.connection().close_reason().is_none())
.unwrap_or(false);
emit_call_debug(&app, "connect:peer_report_timeout", serde_json::json!({
"local_direct_ok": local_direct_ok,
"direct_transport_alive": trust_direct,
"fallback": if trust_direct { "Direct" } else { "Relay" },
}));
trust_direct
} }
} }
}; };