rvoip_transaction_core/server/mod.rs
1/// # Server Transaction Module
2///
3/// This module implements the server-side transaction state machines according to
4/// [RFC 3261 Section 17.2](https://datatracker.ietf.org/doc/html/rfc3261#section-17.2).
5///
6/// ## SIP Server Transactions
7///
8/// Server transactions are created when a SIP element receives a request from a client.
9/// They ensure proper handling of requests, responses, and retransmissions according
10/// to the SIP protocol specifications.
11///
12/// ## Transaction Types
13///
14/// RFC 3261 defines two types of server transactions with different state machines:
15///
16/// 1. **INVITE Server Transactions** (Section 17.2.1): Used for handling session establishment requests.
17/// - More complex due to the three-way handshake (INVITE, response, ACK)
18/// - Uses a four-state machine: Proceeding, Completed, Confirmed, and Terminated
19/// - Uses timers G, H, and I for retransmission and timeout management
20/// - Must handle ACK specially in the Completed state
21///
22/// 2. **Non-INVITE Server Transactions** (Section 17.2.2): Used for all other request types.
23/// - Simpler state machine with three states: Trying, Proceeding, and Completed
24/// - Uses timer J for state management
25/// - No special handling for ACK required
26///
27/// ## Implementation Details
28///
29/// Both transaction types share common infrastructure but implement different state machines:
30///
31/// - `ServerInviteTransaction`: Implements the INVITE server transaction state machine
32/// - `ServerNonInviteTransaction`: Implements the non-INVITE server transaction state machine
33/// - `ServerTransactionData`: Shared data structure for both transaction types
34/// - `CommonServerTransaction`: Common behavior for server transactions
35/// - `ServerTransaction`: Interface for all server transactions
36///
37/// ## Usage
38///
39/// Server transactions are typically created by the `TransactionManager` when it receives
40/// a request from the network. It routes incoming messages to the appropriate transaction
41/// and provides a clean API for the Transaction User (TU) to send responses.
42
43mod common;
44mod invite;
45mod non_invite;
46mod data;
47pub mod builders;
48
49pub use common::*;
50pub use invite::ServerInviteTransaction;
51pub use non_invite::ServerNonInviteTransaction;
52pub use data::{ServerTransactionData, CommandSender, CommandReceiver, CommonServerTransaction};
53
54use async_trait::async_trait;
55use std::net::SocketAddr;
56use std::future::Future;
57use std::pin::Pin;
58use std::sync::Arc;
59
60use crate::error::Result;
61use crate::transaction::{Transaction, TransactionState, TransactionKey, TransactionAsync};
62use rvoip_sip_core::prelude::*;
63use rvoip_sip_core::json::ext::SipMessageJson;
64
65/// Common interface for server transactions, implementing the behavior defined in RFC 3261 Section 17.2.
66///
67/// This trait defines operations that both INVITE and non-INVITE server transactions must support.
68/// It encapsulates the functionality required to process requests, send responses, and track state
69/// according to the SIP specification.
70#[async_trait]
71pub trait ServerTransaction: Transaction + TransactionAsync + Send + Sync + 'static {
72 /// Processes an incoming request associated with this transaction.
73 ///
74 /// This handles various types of requests that may arrive for this transaction:
75 /// - For INVITE server transactions: ACK requests or CANCEL requests
76 /// - For non-INVITE server transactions: Retransmissions of the original request
77 ///
78 /// # Arguments
79 ///
80 /// * `request` - The SIP request to process
81 ///
82 /// # Returns
83 ///
84 /// A Future that resolves to Ok(()) if the request was processed successfully,
85 /// or an Error if there was a problem.
86 fn process_request(&self, request: Request) -> Pin<Box<dyn Future<Output = Result<()>> + Send + '_>>;
87
88 /// Sends a response for this transaction, triggering appropriate state transitions.
89 ///
90 /// According to RFC 3261 Sections 17.2.1 and 17.2.2, sending responses triggers
91 /// specific state transitions based on the response status code:
92 ///
93 /// - For INVITE server transactions:
94 /// - 1xx responses keep the transaction in Proceeding state
95 /// - 2xx responses cause transition to Terminated state
96 /// - 3xx-6xx responses cause transition to Completed state
97 ///
98 /// - For non-INVITE server transactions:
99 /// - In Trying state, 1xx responses cause transition to Proceeding state
100 /// - In Trying or Proceeding state, final responses cause transition to Completed state
101 ///
102 /// # Arguments
103 ///
104 /// * `response` - The SIP response to send
105 ///
106 /// # Returns
107 ///
108 /// A Future that resolves to Ok(()) if the response was sent successfully,
109 /// or an Error if there was a problem.
110 fn send_response(&self, response: Response) -> Pin<Box<dyn Future<Output = Result<()>> + Send + '_>>;
111
112 /// Returns the last response sent by this transaction.
113 ///
114 /// This can be used to handle retransmissions of requests, where the server
115 /// should resend the last response without passing the request to the TU.
116 ///
117 /// # Returns
118 ///
119 /// The last SIP response sent by this transaction, or None if no response has been sent.
120 fn last_response(&self) -> Option<Response>;
121
122 /// Gets the Call-ID from the original request that created this transaction.
123 ///
124 /// Call-ID is a critical dialog identifier used to match ACK with its INVITE.
125 /// According to RFC 3261 section 8.1.1.4, Call-ID must be identical for all
126 /// requests and responses in a dialog, including the ACK for a final response.
127 ///
128 /// # Returns
129 ///
130 /// Some(call_id) if the transaction has an original request with a Call-ID header,
131 /// None otherwise.
132 fn original_request_call_id(&self) -> Option<String> {
133 if let Some(req) = self.original_request_sync() {
134 req.call_id().map(|hdr| hdr.value().to_string())
135 } else {
136 None
137 }
138 }
139
140 /// Gets the From tag from the original request that created this transaction.
141 ///
142 /// From tag is part of the dialog identifiers used to match ACK with its INVITE.
143 /// According to RFC 3261 section 8.1.1.7, the From tag must be identical for all
144 /// requests and responses in a dialog (including ACK and CANCEL).
145 ///
146 /// # Returns
147 ///
148 /// Some(from_tag) if the transaction has an original request with a From tag,
149 /// None otherwise.
150 fn original_request_from_tag(&self) -> Option<String> {
151 if let Some(req) = self.original_request_sync() {
152 req.from_tag()
153 } else {
154 None
155 }
156 }
157
158 /// Gets the To tag from the original request that created this transaction.
159 ///
160 /// To tag may be part of the dialog identifiers used to match ACK with its INVITE.
161 /// In early dialogs, the original INVITE may not have a To tag, but subsequent
162 /// ACKs for final responses will include the To tag from the response.
163 ///
164 /// # Returns
165 ///
166 /// Some(to_tag) if the transaction has an original request with a To tag,
167 /// None otherwise.
168 fn original_request_to_tag(&self) -> Option<String> {
169 if let Some(req) = self.original_request_sync() {
170 req.to_tag()
171 } else {
172 None
173 }
174 }
175
176 /// Synchronous accessor for the original request if it's available without async operations.
177 /// This is an internal helper method that should be implemented by transaction types
178 /// that can provide synchronous access to the original request.
179 ///
180 /// # Returns
181 ///
182 /// Some(Request) if the transaction has cached the original request,
183 /// None if it would require an async operation to retrieve.
184 fn original_request_sync(&self) -> Option<Request> {
185 None
186 }
187}
188
189/// Extension trait for Transaction to safely downcast to ServerTransaction.
190///
191/// This trait provides a convenience method for downcasting any Transaction object
192/// to a ServerTransaction reference, making it easier to work with transaction-specific
193/// functionality without unsafe code.
194pub trait TransactionExt {
195 /// Attempts to downcast to a ServerTransaction reference.
196 ///
197 /// # Returns
198 ///
199 /// Some(&dyn ServerTransaction) if the transaction is a server transaction,
200 /// None otherwise.
201 fn as_server_transaction(&self) -> Option<&dyn ServerTransaction>;
202}
203
204impl<T: Transaction + ?Sized> TransactionExt for T {
205 fn as_server_transaction(&self) -> Option<&dyn ServerTransaction> {
206 use crate::transaction::TransactionKind;
207
208 match self.kind() {
209 TransactionKind::InviteServer | TransactionKind::NonInviteServer => {
210 // Get the Any representation and try downcasting
211 self.as_any().downcast_ref::<Box<dyn ServerTransaction>>()
212 .map(|boxed| boxed.as_ref())
213 .or_else(|| {
214 // Try with specific implementations
215 use crate::server::{ServerInviteTransaction, ServerNonInviteTransaction};
216
217 if let Some(tx) = self.as_any().downcast_ref::<ServerInviteTransaction>() {
218 Some(tx as &dyn ServerTransaction)
219 } else if let Some(tx) = self.as_any().downcast_ref::<ServerNonInviteTransaction>() {
220 Some(tx as &dyn ServerTransaction)
221 } else {
222 None
223 }
224 })
225 },
226 _ => None,
227 }
228 }
229}