Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
4dfab23
feat(transaction): add Accepted state for RFC 6026 §7.1/§7.2 (1/N)
andrico21 Jun 17, 2026
6e53899
feat(transaction): add TimerL + TimerM variants for RFC 6026 (2/N)
andrico21 Jun 17, 2026
efcad8a
feat(transaction): allow RFC 6026 edges in can_transition matrix (3/N)
andrico21 Jun 17, 2026
26dfa6c
feat(transaction): add timer_l + timer_m fields on Transaction (4/N)
andrico21 Jun 17, 2026
f99bda3
feat(transaction): Accepted-state entry handler per RFC 6026 §7.1/§7.…
andrico21 Jun 17, 2026
841d485
feat(transaction): on_timer Accepted dispatch + Completed 2xx guard (…
andrico21 Jun 17, 2026
776d0c3
feat(transaction): server-side RFC 6026 2xx routing + ACK handling (7/N)
andrico21 Jun 17, 2026
ac487af
feat(transaction): client-side RFC 6026 2xx routing + send_ack split …
andrico21 Jun 17, 2026
40671ab
test(transaction): RFC 6026 conformance tests for Accepted state (9/N)
andrico21 Jun 17, 2026
a34bdc7
docs(lib): expand RFC 6026 standards-compliance claim (10/N)
andrico21 Jun 17, 2026
7d65ca4
style: cargo fmt --all cleanup (11/N)
andrico21 Jun 17, 2026
f8e32b0
fix(transaction): pass every 2xx in Accepted to TU per RFC 6026 §7.2 …
andrico21 Jun 17, 2026
1d8a89b
docs(lib): clarify RFC 6026 client-side auto-ACK retention (13/N)
andrico21 Jun 17, 2026
560187a
feat(transaction): mark TransactionState + TransactionTimer #[non_exh…
andrico21 Jun 17, 2026
095d17d
refactor(transaction): tighten Accepted timer dispatch (15/N)
andrico21 Jun 17, 2026
5d3b83a
fix(transaction): log auto-ACK failures instead of silent .ok() (16/N)
andrico21 Jun 17, 2026
ce10e26
docs(transaction): cancel-safety rustdoc on async public APIs (17/N)
andrico21 Jun 17, 2026
2c13347
docs(transaction): correct rustdoc and comment accuracy on touched AP…
andrico21 Jun 17, 2026
d18e17d
refactor(transaction): drop dead wildcard arm in send_ack post-send d…
andrico21 Jun 17, 2026
5dd5ea1
style: cargo fmt cleanup on rebased F1 hunk (20/N)
andrico21 Jun 17, 2026
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: 7 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,13 @@
//!
//! * **RFC 3261** - SIP: Session Initiation Protocol (core specification)
//! * **RFC 3581** - Symmetric Response Routing (rport)
//! * **RFC 6026** - Correct Transaction Handling for 2xx Responses to INVITE
//! * **RFC 6026** - Correct Transaction Handling for 2xx Responses to INVITE.
//! Server `Accepted` state + Timer L (§7.1); client `Accepted` state + Timer M
//! (§7.2); server transactions do not retransmit 2xx (§7.1). Client-side ACK
//! for 2xx is currently auto-issued by the transaction layer for rsipstack
//! 0.5.x backward compatibility — strict §7.2 + RFC 3261 §17.1.1.3 places
//! that responsibility on the TU and the auto-ACK is a candidate for
//! migration to the dialog layer in a follow-up release.
//!
//! ## Performance
//!
Expand Down
85 changes: 65 additions & 20 deletions src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,37 +31,48 @@ pub type TransactionSender = UnboundedSender<Transaction>;
///
/// `TransactionState` represents the various states a SIP transaction can be in
/// during its lifecycle. These states implement the transaction state machines
/// defined in RFC 3261 for both client and server transactions.
/// defined in RFC 3261 for both client and server transactions, with the RFC 6026
/// `Accepted` state for INVITE 2xx response handling.
///
/// # States
///
/// * `Nothing` - Initial state for client transactions created
/// * `Calling` - Initial state for client transactions when request is sent or received
/// * `Trying` - Request has been sent/received, waiting for response/processing
/// * `Proceeding` - Provisional response received/sent (1xx except 100 Trying)
/// * `Completed` - Final response received/sent, waiting for ACK (INVITE) or cleanup
/// * `Confirmed` - ACK received/sent for INVITE transactions
/// * `Accepted` - INVITE transaction received/sent a 2xx final response (RFC 6026 §7.1/§7.2);
/// server transaction waits for ACKs and absorbs 2xx retransmissions from the TU until
/// Timer L fires; client transaction absorbs 2xx retransmissions from the server until
/// Timer M fires.
/// * `Completed` - Final non-2xx response received/sent (RFC 3261 §17), waiting for ACK (server INVITE)
/// or response retransmissions (client). For INVITE 2xx, see `Accepted` (RFC 6026 supersedes RFC 3261 §17.2.1 paragraph 4 / §17.1.1.2).
/// * `Confirmed` - ACK received/sent for INVITE non-2xx (3xx-6xx) transactions
/// * `Terminated` - Transaction has completed and is being cleaned up
///
/// # State Transitions
///
/// ## Client Non-INVITE Transaction
/// ## Client Non-INVITE Transaction (RFC 3261 §17.1.2)
/// ```text
/// Nothing → Calling → Trying → Proceeding → Completed → Terminated
/// ```
///
/// ## Client INVITE Transaction
/// ## Client INVITE Transaction (RFC 3261 §17.1.1 + RFC 6026 §7.2)
/// ```text
/// Nothing → Calling → Trying → Proceeding → Completed → Terminated
/// ↓
/// Confirmed → Terminated
/// Nothing → Calling → Trying → Proceeding ──2xx──→ Accepted ──Timer M──→ Terminated
/// │
/// └──3xx-6xx──→ Completed → Confirmed → Terminated
/// ```
///
/// ## Server INVITE Transaction (RFC 3261 §17.2.1 + RFC 6026 §7.1)
/// ```text
/// Calling → Trying → Proceeding ──2xx──→ Accepted ──Timer L──→ Terminated
/// │
/// └──3xx-6xx──→ Completed ──ACK──→ Confirmed → Terminated
/// ```
///
/// ## Server Transactions
/// ## Server Non-INVITE Transaction (RFC 3261 §17.2.2)
/// ```text
/// Calling → Trying → Proceeding → Completed → Terminated
/// ↓
/// Confirmed → Terminated (INVITE only)
/// ```
///
/// # Examples
Expand All @@ -75,17 +86,28 @@ pub type TransactionSender = UnboundedSender<Transaction>;
/// TransactionState::Calling => println!("Request sent"),
/// TransactionState::Trying => println!("Request sent/received"),
/// TransactionState::Proceeding => println!("Provisional response"),
/// TransactionState::Completed => println!("Final response"),
/// TransactionState::Accepted => println!("2xx accepted; waiting for Timer L/M"),
/// TransactionState::Completed => println!("Final non-2xx response"),
/// TransactionState::Confirmed => println!("ACK received/sent"),
/// TransactionState::Terminated => println!("Transaction complete"),
/// // `#[non_exhaustive]`: downstream code MUST keep a wildcard arm to
/// // remain forward-compatible with future state additions.
/// _ => println!("future RFC-extension state"),
/// }
/// ```
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum TransactionState {
Nothing,
Calling,
Trying,
Proceeding,
/// RFC 6026 §7.1 (server) / §7.2 (client): an INVITE transaction has sent or received
/// a 2xx final response. The server absorbs 2xx retransmissions from the TU and waits
/// for ACK until Timer L (= 64*T1) fires; the client absorbs server-retransmitted 2xx
/// responses until Timer M (= 64*T1) fires. Replaces the RFC 3261 §17.1.1.2 / §17.2.1
/// behaviour that incorrectly routed 2xx through the `Completed` state.
Accepted,
Completed,
Confirmed,
Terminated,
Expand All @@ -98,6 +120,7 @@ impl std::fmt::Display for TransactionState {
TransactionState::Calling => write!(f, "Calling"),
TransactionState::Trying => write!(f, "Trying"),
TransactionState::Proceeding => write!(f, "Proceeding"),
TransactionState::Accepted => write!(f, "Accepted"),
TransactionState::Completed => write!(f, "Completed"),
TransactionState::Confirmed => write!(f, "Confirmed"),
TransactionState::Terminated => write!(f, "Terminated"),
Expand Down Expand Up @@ -174,21 +197,22 @@ impl std::fmt::Display for TransactionType {
/// SIP Transaction Timers
///
/// `TransactionTimer` represents the various timers used in SIP transactions
/// as defined in RFC 3261. These timers ensure reliable message delivery
/// and proper transaction cleanup.
/// as defined in RFC 3261 and RFC 6026. These timers ensure reliable message
/// delivery and proper transaction cleanup.
///
/// # Timer Types
///
/// * `TimerA` - Retransmission timer for client transactions (unreliable transport)
/// * `TimerB` - Transaction timeout timer for client transactions
/// * `TimerC` - Proceeding timeout for client INVITE
/// * `TimerD` - Wait timer for response retransmissions (client)
/// * `TimerE` - Retransmission timer for non-INVITE server transactions
/// * `TimerF` - Transaction timeout timer for non-INVITE server transactions
/// * `TimerK` - Wait timer for ACK (server INVITE transactions)
/// * `TimerG` - Retransmission timer for INVITE server transactions
/// * `TimerK` - Wait timer for ACK (server INVITE non-2xx) / cleanup (client non-INVITE)
/// * `TimerG` - Retransmission timer for INVITE server transactions (non-2xx only per RFC 6026 §7.1)
/// * `TimerL` - RFC 6026 §7.1 server INVITE Accepted-state timer (64*T1)
/// * `TimerM` - RFC 6026 §7.2 client INVITE Accepted-state timer (64*T1)
/// * `TimerCleanup` - Internal cleanup timer for transaction removal
///
/// # Timer Values (RFC 3261)
/// # Timer Values (RFC 3261 + RFC 6026)
///
/// * T1 = 500ms (RTT estimate)
/// * T2 = 4s (maximum retransmit interval)
Expand All @@ -200,8 +224,10 @@ impl std::fmt::Display for TransactionType {
/// * Timer D: 32 seconds for unreliable, 0 for reliable transports
/// * Timer E: starts at T1, doubles up to T2
/// * Timer F: 64*T1 (32 seconds)
/// * Timer G: starts at T1, doubles up to T2
/// * Timer G: starts at T1, doubles up to T2 (non-2xx final response retransmits only)
/// * Timer K: T4 for unreliable, 0 for reliable transports
/// * **Timer L: 64*T1 (32 seconds) — RFC 6026 §7.1**
/// * **Timer M: 64*T1 (32 seconds) — RFC 6026 §7.2**
///
/// # Examples
///
Expand Down Expand Up @@ -233,6 +259,9 @@ impl std::fmt::Display for TransactionType {
/// TransactionTimer::TimerB(key) => {
/// println!("Transaction {} timed out", key);
/// },
/// // `#[non_exhaustive]`: downstream code MUST keep a wildcard arm to
/// // remain forward-compatible with future timer additions (e.g. RFC
/// // extensions like Timer L / Timer M / future RFC variants).
/// _ => {}
/// }
/// # Ok(())
Expand All @@ -246,13 +275,25 @@ impl std::fmt::Display for TransactionType {
/// * Cancelled when leaving states or receiving responses
/// * Fire events that drive state machine transitions
/// * Handle retransmissions and timeouts
#[non_exhaustive]
pub enum TransactionTimer {
TimerA(TransactionKey, Duration),
TimerB(TransactionKey),
TimerC(TransactionKey),
TimerD(TransactionKey),
TimerK(TransactionKey),
TimerG(TransactionKey, Duration),
/// RFC 6026 §7.1: server INVITE Accepted-state timer. Fires once at 64*T1
/// after a 2xx final response is sent, then transitions the transaction to
/// Terminated. Absorbs ACKs for the 2xx and late 2xx retransmissions from
/// the TU. Replaces the RFC 3261 §17.2.1 Timer K (T4) usage for 2xx
/// responses, which was too short to handle proxy-chain ACK fan-in.
TimerL(TransactionKey),
/// RFC 6026 §7.2: client INVITE Accepted-state timer. Fires once at 64*T1
/// after a 2xx final response is received, then transitions the
/// transaction to Terminated. Absorbs server-retransmitted 2xx responses
/// (the TU is responsible for the ACK).
TimerM(TransactionKey),
TimerCleanup(TransactionKey),
}

Expand All @@ -265,6 +306,8 @@ impl TransactionTimer {
TransactionTimer::TimerD(key) => key,
TransactionTimer::TimerG(key, _) => key,
TransactionTimer::TimerK(key) => key,
TransactionTimer::TimerL(key) => key,
TransactionTimer::TimerM(key) => key,
TransactionTimer::TimerCleanup(key) => key,
}
}
Expand All @@ -283,6 +326,8 @@ impl std::fmt::Display for TransactionTimer {
write!(f, "TimerG: {} {}", key, duration.as_millis())
}
TransactionTimer::TimerK(key) => write!(f, "TimerK: {}", key),
TransactionTimer::TimerL(key) => write!(f, "TimerL: {}", key),
TransactionTimer::TimerM(key) => write!(f, "TimerM: {}", key),
TransactionTimer::TimerCleanup(key) => write!(f, "TimerCleanup: {}", key),
}
}
Expand Down
Loading