Since #169, DialogState::Confirmed of a server dialog carries an empty response (no CSeq) for the initial INVITE and every re-INVITE #173
Description
Summary
On the UAS side, DialogState::Confirmed(id, response) is built from the server INVITE transaction's last_response after the ACK arrives. Since #169 the matching ACK terminates the Accepted transaction, and cleanup() takes last_response before the dialog reads it. So every Confirmed of a server dialog, for the initial INVITE and for each re-INVITE, carries Response::default(): status 200 with no headers at all, so no CSeq, no To tag, no Contact, no body.
An application that matches Confirmed to the INVITE it confirms (by CSeq, for example to know which re-INVITE's offer/answer is now in effect) can no longer do it. Reading the CSeq of that response returns MissingHeader("CSeq").
Reproduced on current main (5ef8ea6) over UDP and TCP. The client side is not affected: a UAC builds Confirmed from the 2xx it just received.
Expected
DialogStatedocs (src/dialog/dialog.rs:88): "Confirmed- Dialog is established and confirmed (2xx response received/sent and ACK sent/received)". The variant carries that 2xx (Confirmed(DialogId, crate::sip::Response),dialog.rs:119), and the UAS code passestx.last_responsefor exactly that purpose.- RFC 3261 §13.2.2.4: the ACK's CSeq number "MUST be the same as the INVITE being acknowledged". The CSeq of the 2xx is what ties a
Confirmedto its INVITE. - Before fix(transaction): end the server Accepted state on the matching ACK (review follow-up to #164) #169 this held: on f27b7c8 (the feat(transaction): RFC 6026 Accepted state + Timer L/M (adopted from #128) #164 merge) the
Confirmedof a server dialog carries the 2xx withCSeq: 1 INVITE; on 52ac300 (the fix(transaction): end the server Accepted state on the matching ACK (review follow-up to #164) #169 merge) it carries an empty response (same test, below).
What happens
At 5ef8ea6:
Transaction::on_received_request, Accepted arm (src/transaction/transaction.rs:901-919): a matching ACK callsself.transition(TransactionState::Terminated)(line 918, added by fix(transaction): end the server Accepted state on the matching ACK (review follow-up to #164) #169) and only then returns the ACK to the dialog.transitiontoTerminatedcallsself.cleanup()(transaction.rs:1487), which hands the response tofinished_transactionswithself.last_response.take()(transaction.rs:1615-1617).last_responseis nowNone.- The dialog then builds
DialogState::Confirmed(self.id(), tx.last_response.clone().unwrap_or_default()):src/dialog/server_dialog.rs:865(re-INVITE) and:905(initial INVITE), and the same insrc/dialog/invite_dialog.rs:863and:903.unwrap_or_default()yieldsResponse::default()(src/sip/message.rs:586-595).
Sequence: INVITE (CSeq 1) / 200 / ACK. The Confirmed state's response has no CSeq. Same for a re-INVITE (CSeq 2) / 200 / ACK.
A fix with tests is in the linked PR.
Activity