rvoip_transaction_core/client/mod.rs
1/// # Client Transaction Module
2///
3/// This module implements the client-side transaction state machines according to
4/// [RFC 3261 Section 17.1](https://datatracker.ietf.org/doc/html/rfc3261#section-17.1).
5///
6/// ## SIP Client Transactions
7///
8/// Client transactions are initiated by the Transaction User (TU) when it wants to send a request.
9/// They ensure reliable delivery of requests, handle retransmissions, and deliver responses
10/// back to the TU.
11///
12/// ## Transaction Types
13///
14/// RFC 3261 defines two types of client transactions with different state machines:
15///
16/// 1. **INVITE Client Transactions** (Section 17.1.1): Used for session establishment.
17/// - More complex due to the three-way handshake required (INVITE, response, ACK)
18/// - Special handling for ACK generation on non-2xx responses
19/// - Unique timer requirements (Timers A, B, D)
20///
21/// 2. **Non-INVITE Client Transactions** (Section 17.1.2): Used for all other request types.
22/// - Simpler state machine
23/// - No ACK generation
24/// - Different timer requirements (Timers E, F, K)
25///
26/// ## Implementation Details
27///
28/// Both transaction types share common infrastructure but implement different state machines:
29///
30/// - `ClientInviteTransaction`: Implements the INVITE client transaction state machine
31/// - `ClientNonInviteTransaction`: Implements the non-INVITE client transaction state machine
32/// - `ClientTransactionData`: Shared data structure for both transaction types
33/// - `CommonClientTransaction`: Common behavior for client transactions
34/// - `ClientTransaction`: Interface for all client transactions
35///
36/// ## Usage
37///
38/// Client transactions are typically created and managed by the `TransactionManager`, which routes
39/// incoming messages to the appropriate transaction and exposes a clean API for the TU.
40
41mod common;
42mod invite;
43mod non_invite;
44mod data;
45pub mod builders;
46
47pub use common::*;
48pub use invite::ClientInviteTransaction;
49pub use non_invite::ClientNonInviteTransaction;
50pub use data::{ClientTransactionData, CommandSender, CommandReceiver, CommonClientTransaction};
51
52use async_trait::async_trait;
53use std::net::SocketAddr;
54use std::sync::Arc;
55use std::future::Future;
56use std::pin::Pin;
57
58use crate::error::Result;
59use crate::transaction::{Transaction, TransactionState, TransactionKey, TransactionAsync};
60use rvoip_sip_core::prelude::*;
61use rvoip_sip_core::Request;
62
63/// Common interface for client transactions, implementing the behavior defined in RFC 3261 Section 17.1.
64///
65/// This trait defines operations that both INVITE and non-INVITE client transactions must support.
66/// It encapsulates the functionality required to initiate transactions, process responses,
67/// and track state according to the SIP specification.
68pub trait ClientTransaction: Transaction + Send + Sync + 'static {
69 /// Initiates the transaction by sending the first request.
70 ///
71 /// For INVITE transactions, this starts Timers A/B and moves the transaction to the Calling state.
72 /// For non-INVITE transactions, this starts Timers E/F and moves the transaction to the Trying state.
73 ///
74 /// # Returns
75 ///
76 /// A Future that resolves to Ok(()) if the transaction was initiated successfully,
77 /// or an Error if there was a problem.
78 fn initiate(&self) -> Pin<Box<dyn Future<Output = Result<()>> + Send + '_>>;
79
80 /// Processes an incoming response for this transaction.
81 ///
82 /// This method is called when a response is received that matches this transaction
83 /// according to the rules in RFC 3261 Section 17.1.3.
84 ///
85 /// # Arguments
86 ///
87 /// * `response` - The SIP response to process
88 ///
89 /// # Returns
90 ///
91 /// A Future that resolves to Ok(()) if the response was processed successfully,
92 /// or an Error if there was a problem.
93 fn process_response(&self, response: Response) -> Pin<Box<dyn Future<Output = Result<()>> + Send + '_>>;
94
95 /// Returns the original request that initiated this transaction.
96 ///
97 /// # Returns
98 ///
99 /// A Future that resolves to the original SIP request, or None if it's not available.
100 fn original_request<'a>(&'a self) -> Pin<Box<dyn Future<Output = Option<Request>> + Send + 'a>>;
101
102 /// Returns the last response received by this transaction.
103 ///
104 /// # Returns
105 ///
106 /// A Future that resolves to the last received SIP response, or None if no response
107 /// has been received yet.
108 fn last_response<'a>(&'a self) -> Pin<Box<dyn Future<Output = Option<Response>> + Send + 'a>>;
109}
110
111/// Extension trait for Transaction to safely downcast to ClientTransaction.
112///
113/// This trait provides a convenience method for downcasting any Transaction object
114/// to a ClientTransaction reference, making it easier to work with transaction-specific
115/// functionality without unsafe code.
116pub trait TransactionExt {
117 /// Attempts to downcast to a ClientTransaction reference.
118 ///
119 /// # Returns
120 ///
121 /// Some(&dyn ClientTransaction) if the transaction is a client transaction,
122 /// None otherwise.
123 fn as_client_transaction(&self) -> Option<&dyn ClientTransaction>;
124}
125
126impl<T: Transaction + ?Sized> TransactionExt for T {
127 fn as_client_transaction(&self) -> Option<&dyn ClientTransaction> {
128 use crate::transaction::TransactionKind;
129
130 match self.kind() {
131 TransactionKind::InviteClient | TransactionKind::NonInviteClient => {
132 // Get the Any representation and try downcasting
133 self.as_any().downcast_ref::<Box<dyn ClientTransaction>>()
134 .map(|boxed| boxed.as_ref())
135 .or_else(|| {
136 // Try with specific implementations
137 use crate::client::{ClientInviteTransaction, ClientNonInviteTransaction};
138
139 if let Some(tx) = self.as_any().downcast_ref::<ClientInviteTransaction>() {
140 Some(tx as &dyn ClientTransaction)
141 } else if let Some(tx) = self.as_any().downcast_ref::<ClientNonInviteTransaction>() {
142 Some(tx as &dyn ClientTransaction)
143 } else {
144 None
145 }
146 })
147 },
148 _ => None,
149 }
150 }
151}