fix(signal): add call_id to Hangup — prevents stale hangup killing new calls

Root cause: Hangup had no call_id field. The relay forwarded hangups to
ALL active calls for a user. When user A hung up call 1 and user B
immediately placed call 2, the relay's processing of A's hangup would
also kill call 2 (race window ~1-2s).

Fix: add optional call_id to Hangup (backwards-compatible via serde
skip_serializing_if). When present, the relay only ends the named call.
Old clients send call_id=None and get the legacy broadcast behavior.

Also: clear pending_path_report in Hangup recv handler and
internal_deregister to prevent stale oneshot channels from blocking
subsequent call setups.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-04-12 16:39:21 +04:00
parent d89376016a
commit a798634b3d
7 changed files with 58 additions and 20 deletions

View File

@@ -971,6 +971,7 @@ async fn internal_deregister(
sig.incoming_caller_fp = None;
sig.incoming_caller_alias = None;
sig.pending_reflect = None;
sig.pending_path_report = None;
sig.own_reflex_addr = None;
if !keep_desired {
sig.desired_relay_addr = None;
@@ -1151,10 +1152,14 @@ fn do_register_signal(
}),
);
}
Ok(Some(SignalMessage::Hangup { reason })) => {
Ok(Some(SignalMessage::Hangup { reason, .. })) => {
tracing::info!(?reason, "signal: Hangup");
emit_call_debug(&app_clone, "recv:Hangup", serde_json::json!({ "reason": format!("{:?}", reason) }));
let mut sig = signal_state.lock().await; sig.signal_status = "registered".into(); sig.incoming_call_id = None; sig.ipv6_endpoint = None;
let mut sig = signal_state.lock().await;
sig.signal_status = "registered".into();
sig.incoming_call_id = None;
sig.ipv6_endpoint = None;
sig.pending_path_report = None;
let _ = app_clone.emit("signal-event", serde_json::json!({"type":"hangup"}));
}
Ok(Some(SignalMessage::MediaPathReport { call_id, direct_ok, race_winner })) => {
@@ -1853,6 +1858,7 @@ async fn hangup_call(
match transport
.send_signal(&SignalMessage::Hangup {
reason: wzp_proto::HangupReason::Normal,
call_id: None,
})
.await
{