You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A dropped do_invite does nothing when the application already removed the dialog: no CANCEL, no Terminated, and a 2xx is never BYE'd #182
Dropping the DialogLayer::do_invite future is the documented way to abandon an outgoing call, and since #162 / #171 the drop guard CANCELs the INVITE (after the first provisional when none has arrived yet), reports Terminated(UacCancel), and ACKs and BYEs a 2xx that crosses the CANCEL. The guard finds the dialog by removing it from the DialogLayer, though, and does nothing at all when it is no longer there.
So an application that calls the public DialogLayer::remove_dialog for the pending call (for example from its own hangup or cleanup path, with the id the Calling state reported) and then drops the do_invite future gets none of it:
no CANCEL is ever sent, so the callee keeps ringing until it gives up;
no Terminated is reported: the state channel just closes;
a 2xx to the INVITE is never BYE'd, so the callee is left in a session nobody owns. In Calling it is not even ACKed.
Expected
The same as for a dialog that is still in the layer. Removing the dialog from the registry does not end its INVITE:
RFC 3261 §9.1: "If no provisional response has been received, the CANCEL request MUST NOT be sent; rather, the client MUST wait for the arrival of a provisional response before sending the request." A CANCEL has no effect on a request that already got a final response.
RFC 3261 §13.2.2.4 / §15: the UAC ACKs every 2xx to its INVITE, and a UAC that does not want the session it established ends it with a BYE.
RFC 5407 §3.1.2 describes exactly this CANCEL / 2xx race: the 2xx is ACKed and the session is ended with a BYE.
do_invite (src/dialog/invitation.rs:675) registers the dialog under its initial id (lines 683-685) and creates DialogGuardForUnconfirmed (line 687). The Calling state reports that same id (src/dialog/invite_dialog.rs:271).
remove_dialog (src/dialog/dialog_layer.rs:509-513) removes the entry and runs on_remove, which only cancels the dialog's cancel_token (src/dialog/dialog.rs:1748-1752). The INVITE transaction keeps running inside do_invite.
When the do_invite future is then dropped, DialogGuardForUnconfirmed::drop (src/dialog/invitation.rs:192) starts with let Some(dlg) = self.dialog_layer_inner.dialogs.remove(&self.id.to_string()) else { return; }; (line 194). The entry is gone, so it returns, and the INVITE transaction is dropped with the guard.
A 2xx that arrives afterwards finds no transaction. After a provisional, the endpoint answers it with the placeholder ACK the dropped transaction left behind (src/transaction/transaction.rs:1590-1613, src/transaction/endpoint.rs:422-446); in Calling there is none, so not even an ACK is sent. Nothing sends a BYE.
Observed with a raw UDP peer (remove the dialog under the Calling id, drop the future, wait 300 ms, send 200 OK):
before any provisional: peer got [] after 200; states after drop []; layer len 0
after a 180: peer got ["ACK"] after 200; states after drop []; layer len 0
Activity