Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/transaction/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ pub struct EndpointOption {
/// (RFC 7989) needs no switch: dialogs participate only when the
/// application supplies a UUID or the peer sends a Session-ID header.
pub history_info_enabled: bool,
/// Whether a client INVITE transaction ACKs a 2xx final itself (default `true`).
/// With `false` a 2xx moves the transaction straight to
/// Terminated: no automatic ACK, no stored ACK.
/// RFC 3261 section 17.1.1.2: the 2xx ACK is the TU's, end-to-end;
/// proxies (section 16) must not ACK a 2xx and must see retransmitted 2xx.
/// `true` keeps the UA behavior the dialog layer relies on.
pub auto_ack_2xx: bool,
}

impl Default for EndpointOption {
Expand All @@ -61,6 +68,7 @@ impl Default for EndpointOption {
callid_suffix: None,
callid_format: CallIdFormat::default(),
history_info_enabled: false,
auto_ack_2xx: true,
}
}
}
Expand Down
24 changes: 21 additions & 3 deletions src/transaction/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,16 @@ impl Transaction {
}
_ => {
if self.transaction_type == TransactionType::ClientInvite {
TransactionState::Completed
if resp.status_code.kind() == StatusCodeKind::Successful
&& !self.endpoint_inner.option.auto_ack_2xx
{
// auto_ack_2xx = false (proxy mode): a 2xx terminates
// the client INVITE transaction at once; the ACK is
// the TU's job (RFC 3261 section 17.1.1.2).
TransactionState::Terminated
} else {
TransactionState::Completed
}
} else {
TransactionState::Terminated
}
Expand Down Expand Up @@ -1105,8 +1114,17 @@ impl Transaction {
) && self.last_ack.is_none()
{
if let Some(ref resp) = self.last_response {
if let Ok(ack) = self.endpoint_inner.make_ack(&self.original, resp) {
self.last_ack.replace(ack);
// auto_ack_2xx = false (proxy mode): store no ACK
// for a 2xx, so retransmitted 2xx fall through
// to the TU instead of being absorbed or re-ACKed
// here (RFC 3261 section 17.1.1.2).
let auto_ack = self.endpoint_inner.option.auto_ack_2xx
|| resp.status_code.kind() != StatusCodeKind::Successful;
if auto_ack {
if let Ok(ack) = self.endpoint_inner.make_ack(&self.original, resp)
{
self.last_ack.replace(ack);
}
}
}
}
Expand Down