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 late ACK of a re-INVITE 2xx is routed to the next re-INVITE, and the call is ended with a BYE #165
A server INVITE transaction that waits for the ACK of its 2xx is found by dialog only (EndpointInner::waiting_ack, one entry per dialog). When the ACK of re-INVITE N arrives after re-INVITE N+1 has been answered, it is routed to N+1's transaction. Since #155 that transaction ignores it (the CSeq does not match), so it confirms nothing. Re-INVITE N never sees its ACK: it retransmits its 2xx until 64*T1, and since #149 the dialog is then terminated with TerminatedReason::Timeout and a BYE. An established call is torn down although the peer ACKed every 2xx.
Reproduced on current main (8a89e9e) over UDP. The routing is the same on every transport.
Expected
RFC 3261 §13.2.2.4: "The sequence number of the CSeq header field MUST be the same as the INVITE being acknowledged, but the CSeq method MUST be ACK."
RFC 3261 §13.3.1.4: "Response retransmissions cease when an ACK request for the response is received." and "If the server retransmits the 2xx response for 64*T1 seconds without receiving an ACK, the dialog is confirmed, but the session SHOULD be terminated."
The 2xx ACK is sent by the UAC core, outside the INVITE transaction (§13.2.2.4, §17.1.1.2). A UAC can send its next re-INVITE right after that ACK, and over UDP the two can arrive in either order.
So the ACK with CSeq N must stop the retransmissions of the 2xx to the INVITE with CSeq N, whatever was received in between.
Transaction::transition (src/transaction/transaction.rs:1070-1074) inserts the transaction into waiting_ack under its dialog id when it enters Completed. A newer re-INVITE on the same dialog replaces the entry of the older one.
EndpointInner::on_received_message (src/transaction/endpoint.rs:382) routes every ACK to waiting_ack.get(&dialog_id), whatever its CSeq.
Removal is by dialog too, so whichever transaction finishes first also removes the other one's route: transaction.rs:1093 (Confirmed), transaction.rs:1168 (cleanup) and endpoint.rs:397 (absorbed ACK).
Sequence: INVITE 1 / 200 / ACK 1, then re-INVITE 2 answered 200, re-INVITE 3 answered 200, then ACK 2, then ACK 3. On main the 200 to CSeq 2 is still retransmitted after ACK 2, and after 64*T1 the UAS sends a BYE and the dialog reports Terminated(_, Timeout) (test below, with its first assertion disabled). With the default timers that is 32 s into a call that is working.
Activity