Repository navigation
Conversation
When the do_invite future is dropped before any provisional response, the guard drops the INVITE transaction and sends nothing. The INVITE may still have reached the callee: its 180 gets no CANCEL, and a 2xx is neither ACKed nor ended, so the callee keeps ringing or answers into dead air. A CANCEL must not be sent before a provisional response; the UAC has to wait for one and CANCEL then (RFC 3261 §9.1). Keep the INVITE transaction, without retransmissions, until its first response (up to 64*T1): on a provisional, cancel it as a call dropped while ringing; on a 2xx, ACK it and send a BYE; any other final response is ACKed by the transaction. Terminated(UacCancel) is still reported at once, and nothing is sent when no response arrives.
shenjinti
added a commit
that referenced
this pull request
Oct 6, 2026
…rst-provisional-rebased fix: CANCEL a dropped INVITE after its first provisional response (rebase of #168)
Contributor
|
Adopted via #171 (clean rebase, no conflicts). Merged with all tests green — thank you! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #166.
Problem
When the
do_invitefuture is dropped while the dialog isCalling(no response yet), the guard drops the INVITE transaction and sends nothing. A later 180 gets no CANCEL, a later 2xx is neither ACKed nor BYE'd, and a later non-2xx final is not ACKed. The callee keeps ringing, or answers into dead air.Spec
Trying/Earlydrop).Fix (src/dialog/invitation.rs, src/dialog/invite_dialog.rs)
DialogGuardForUnconfirmed::drop:Callingnow shares theTrying/Earlyarm. ForCallingit still reportsTerminated(UacCancel)at once, before spawning, as before.Callingonly, first stops the INVITE retransmissions and waits up to 64*T1 for the first response (wait_response, which iswait_final_responsewith afinal_onlyflag):Trying/Earlycode unchanged (CANCEL, 2 s settle window, wait for the final response, ACK and BYE a crossing 2xx). Its 64*T1 final-response window starts after the provisional. The secondTerminated(UacCancel)that path reports is skipped forCalling, which already reported it.InviteDialog::send_cancel, split out ofcancel()without its state check: the dialog is alreadyTerminatedwhen aCallingdrop sends its CANCEL. ForTrying/Earlythe guard already matched the state, so the CANCEL sent is the same as before.bye_if_2xx, shared by both paths.Contract / coverage
Terminated(UacCancel)at once, as before, and once. Nothing is sent before a provisional, and the INVITE is no longer retransmitted.Trying), an ACK and a BYE for a 2xx, the transaction's ACK for 3xx-6xx. NoConfirmedis reported for an abandoned call.crate::platformprimitives already used by this arm.Trying/Earlydrop (test_2xx_before_cancel_response_is_acked_and_byed,test_2xx_after_cancel_response_is_acked_and_byed,test_2xx_after_cancel_settle_window_is_acked_and_byed,test_cancel_answered_487_sends_no_byepass unchanged), theConfirmeddrop (hangup),InviteDialog::cancel()for applications, anddo_invite_async, which does not use the guard.Trying/Earlypath: only the first 2xx of a forked INVITE is BYE'd; withEndpointOption::auto_ack_2xx = falsethe 2xx is not ACKed before the BYE; a reliable provisional (100rel) gets no PRACK before the CANCEL; the task does not watch the endpoint's cancellation token. These belong to both paths and are left for separate changes.Tests
src/dialog/tests/test_cancel_2xx_race.rs, with the file's raw UDP peer (default T1 = 500 ms).run_dropped_before_provisional(first)dropsdo_inviteright after the INVITE arrives, assertsTerminated(UacCancel)within 200 ms and that the peer receives nothing for 1.1 s (over twice T1), then sendsfirst:test_dropped_before_provisional_is_cancelled_after_the_180: a CANCEL with the INVITE's CSeq follows, then 200 to it and 487 to the INVITE, whose ACK follows; no BYE.test_dropped_before_provisional_2xx_is_acked_and_byed: the 200 is ACKed and a BYE follows, both in the 2xx's dialog.test_dropped_before_provisional_final_failure_is_acked_only: the 486 is ACKed; no BYE.Each also asserts that no second
Terminatedand noConfirmedis reported. Onmain:A 2xx that crosses the deferred CANCEL runs the same code as
run_crossing_2xx, so it is not tested again here.Checks
cargo test --features bench: 369 lib tests passed, 0 failed (366 onmain), and 65 doc tests passed. Plaincargo testbuilds too.cargo check --no-default-features --features platform-embassy: no warnings, as onmain.cargo clippy --features bench --all-targets: the same 114 warnings as onmain, none in the changed code.rustfmt --checkon the changed files: clean.