rvoip_transaction_core/
lib.rs

1pub mod client;
2pub mod error;
3pub mod server;
4pub mod manager;
5pub mod transaction;
6pub mod timer;
7pub mod utils;
8pub mod method;
9pub mod transport;
10pub mod dialog;
11
12// Re-export core types
13pub use error::{Error, Result};
14pub use manager::TransactionManager;
15pub use transaction::{
16    Transaction, TransactionAsync, TransactionState, TransactionKind, TransactionKey, TransactionEvent,
17    InternalTransactionCommand,
18};
19pub use client::{ClientInviteTransaction, ClientNonInviteTransaction, ClientTransaction, TransactionExt as ClientTransactionExt};
20pub use server::{ServerInviteTransaction, ServerNonInviteTransaction, ServerTransaction, TransactionExt as ServerTransactionExt};
21pub use timer::{Timer, TimerManager, TimerFactory, TimerSettings, TimerType};
22pub use transport::TransportManager;
23pub use rvoip_sip_transport::transport::TransportType;
24pub use transport::{
25    TransportCapabilities, TransportInfo, NetworkInfoForSdp, 
26    WebSocketStatus, TransportCapabilitiesExt
27};
28
29/// Convenient re-exports for request and response builders
30pub mod builders {
31    /// Client-side request builders for common SIP operations
32    pub use crate::client::builders::{
33        InviteBuilder, ByeBuilder, RegisterBuilder, InDialogRequestBuilder,
34        quick as client_quick
35    };
36    
37    /// Server-side response builders for common SIP operations
38    pub use crate::server::builders::{
39        ResponseBuilder, InviteResponseBuilder, RegisterResponseBuilder,
40        quick as server_quick
41    };
42    
43    /// Dialog utility functions for bridging dialog-core integration
44    pub mod dialog_utils {
45        pub use crate::dialog::{
46            DialogRequestTemplate, DialogTransactionContext,
47            request_builder_from_dialog_template, response_builder_for_dialog_transaction,
48            extract_dialog_template_from_request, create_dialog_transaction_context,
49            helpers
50        };
51    }
52    
53    /// Quick dialog functions for one-liner dialog operations  
54    pub mod dialog_quick {
55        pub use crate::dialog::quick::{
56            bye_for_dialog, refer_for_dialog, update_for_dialog, info_for_dialog,
57            notify_for_dialog, message_for_dialog, reinvite_for_dialog,
58            response_for_dialog_transaction
59        };
60    }
61}
62
63/// # SIP Transaction Layer
64/// 
65/// This crate implements the SIP transaction layer as defined in [RFC 3261](https://datatracker.ietf.org/doc/html/rfc3261),
66/// providing reliable message delivery and state management for SIP request-response exchanges.
67///
68/// ## Purpose and Responsibilities
69/// 
70/// The transaction layer in a SIP stack has several key responsibilities:
71/// 
72/// 1. **Message Reliability**: Ensuring messages are delivered reliably over unreliable transports (e.g., UDP)
73///    by handling retransmissions and timeouts.
74/// 
75/// 2. **State Management**: Implementing the state machines defined in RFC 3261 for handling different types
76///    of SIP transactions (INVITE client/server, non-INVITE client/server).
77/// 
78/// 3. **Transaction Matching**: Correctly matching requests and responses to their corresponding transactions
79///    based on SIP headers (Via, CSeq, etc.) according to the rules defined in RFC 3261 Section 17.1.3 and 17.2.3.
80/// 
81/// 4. **Timer Management**: Managing various timers required by the SIP protocol for retransmissions,
82///    transaction timeouts, and cleanup operations.
83/// 
84/// 5. **ACK Handling**: Special processing for ACK messages, which are treated differently depending on
85///    whether they acknowledge 2xx or non-2xx responses.
86/// 
87/// 6. **Transaction User Interface**: Providing a clean API for the Transaction User (TU) layer to
88///    send requests, receive responses, and handle events.
89///
90/// ## Architecture
91///
92/// The transaction layer sits between the transport layer and the transaction user (TU) layer in the SIP stack:
93///
94/// ```text
95/// +--------------------------------------+
96/// |        Transaction User (TU)         |
97/// |  (dialog management, call control)   |
98/// +--------------------------------------+
99///                    |
100///                    v
101/// +--------------------------------------+
102/// |        Transaction Layer             |
103/// |  (this crate: transaction-core)      |
104/// +--------------------------------------+
105///                    |
106///                    v
107/// +--------------------------------------+
108/// |        Transport Layer               |
109/// |  (UDP, TCP, TLS, WebSocket)          |
110/// +--------------------------------------+
111///                    |
112///                    v
113/// +--------------------------------------+
114/// |          Network                     |
115/// +--------------------------------------+
116/// ```
117///
118/// ### Transaction vs. Dialog Layer
119/// 
120/// It's important to understand the separation between the transaction layer and dialog layer in SIP:
121/// 
122/// - **Transaction Layer** (this library): Handles individual request-response exchanges and ensures
123///   reliable message delivery. Transactions are short-lived with well-defined lifecycles.
124/// 
125/// - **Dialog Layer** (implemented in session-core): Maintains long-lived application state across
126///   multiple transactions. Dialogs track the relationship between endpoints using Call-ID, tags,
127///   and sequence numbers.
128/// 
129/// This separation allows the transaction layer to focus solely on message reliability and state management,
130/// while the dialog layer handles higher-level application logic.
131///
132/// ### Relationship to Other Libraries
133/// 
134/// In the RVOIP project, transaction-core interacts with:
135/// 
136/// - **sip-core**: Provides SIP message parsing, construction, and basic types
137/// - **sip-transport**: Handles the actual sending and receiving of SIP messages
138/// - **session-core**: Consumer of transaction services, implementing dialog management
139/// 
140/// The transaction layer isolates the transport details from higher layers while providing 
141/// transaction state management that higher layers don't need to implement.
142///
143/// #### Transaction Core and Session Core Relationship
144/// 
145/// The `transaction-core` and `session-core` libraries are designed to work together while maintaining
146/// clear separation of concerns:
147/// 
148/// - **transaction-core** handles individual message exchanges with reliability and retransmission
149///   according to RFC 3261 Section 17, ensuring messages are delivered and properly tracked.
150/// 
151/// - **session-core** builds on top of transaction-core to implement dialog management (RFC 3261 Section 12)
152///   and higher-level session concepts like calls and registrations.
153/// 
154/// Typically, an application would:
155/// 
156/// 1. Create a `TransactionManager` from transaction-core
157/// 2. Pass it to a `SessionManager` from session-core 
158/// 3. Work primarily with the SessionManager's higher-level API
159/// 4. Receive events from both layers (transaction events and session events)
160/// 
161/// This layered architecture allows each component to focus on its specific responsibilities
162/// while providing clean integration points between layers.
163///
164/// ## Library Organization
165/// 
166/// The codebase is organized into several modules:
167/// 
168/// - **manager**: Contains the `TransactionManager`, the main entry point for the library
169/// - **client**: Implements client transaction types (INVITE and non-INVITE)
170/// - **server**: Implements server transaction types (INVITE and non-INVITE)
171/// - **transaction**: Defines common transaction traits, states, and events
172/// - **timer**: Implements timer management for retransmissions and timeouts
173/// - **method**: Handles special method-specific behavior (CANCEL, ACK, etc.)
174/// - **utils**: Utility functions for transaction processing
175/// - **error**: Error types and results for the library
176/// 
177/// Most users will primarily interact with the `TransactionManager` class, which provides
178/// the public API for creating and managing transactions.
179///
180/// ## Key Components
181///
182/// * [`TransactionManager`]: Central coordinator for all transactions. Responsible for:
183///   - Creating and tracking transactions
184///   - Routing incoming messages to the right transaction
185///   - Handling "stray" messages that don't match any transaction
186///   - Providing a unified interface to the Transaction User
187///
188/// * Transaction Types:
189///   * [`ClientInviteTransaction`]: Implements RFC 3261 section 17.1.1 state machine
190///   * [`ClientNonInviteTransaction`]: Implements RFC 3261 section 17.1.2 state machine
191///   * [`ServerInviteTransaction`]: Implements RFC 3261 section 17.2.1 state machine
192///   * [`ServerNonInviteTransaction`]: Implements RFC 3261 section 17.2.2 state machine
193///
194/// * Timer Management:
195///   * [`TimerManager`]: Manages transaction timers as per RFC 3261
196///   * [`TimerFactory`]: Creates appropriate timers for different transaction types
197///   * [`TimerSettings`]: Configures timer durations (T1, T2, etc.)
198///
199/// ## Transaction Matching
200/// 
201/// The transaction layer needs to match incoming messages to the right transaction. According to RFC 3261:
202/// 
203/// - **For Responses**: Matched using the branch parameter, sent-by value in the top Via header, 
204///   and CSeq method.
205/// 
206/// - **For ACK to non-2xx**: Matched to the original INVITE transaction. The branch parameter and other
207///   identifiers remain the same as the INVITE.
208/// 
209/// - **For ACK to 2xx**: Not matched to any transaction - handled by the TU (dialog layer).
210/// 
211/// - **For CANCEL**: Creates a new transaction but matches to an existing INVITE transaction
212///   with the same identifiers (except method).
213/// 
214/// The `TransactionManager` implements these matching rules to route messages to the appropriate
215/// transaction instance.
216///
217/// ## Usage Examples
218///
219/// ### 1. Basic Client Transaction
220///
221/// ```
222/// # mod doctest_helpers {
223/// #   use rvoip_sip_core::{Method, Message as SipMessage, Request as SipCoreRequest, Response as SipCoreResponse, Uri};
224/// #   use rvoip_sip_core::builder::{SimpleRequestBuilder, SimpleResponseBuilder};
225/// #   use rvoip_sip_core::types::{
226/// #       header::TypedHeader,
227/// #       content_length::ContentLength as ContentLengthHeaderType,
228/// #       status::StatusCode,
229/// #       param::Param,
230/// #       via::Via,
231/// #   };
232/// #   use rvoip_sip_transport::{Transport, Error as TransportError, TransportEvent as TransportLayerEvent};
233/// #   use std::net::SocketAddr;
234/// #   use std::sync::Arc;
235/// #   use tokio::sync::{Mutex, mpsc};
236/// #   use std::collections::VecDeque;
237/// #   use async_trait::async_trait;
238/// #   use std::str::FromStr;
239/// #   use uuid::Uuid;
240///
241/// #   #[derive(Debug, Clone)]
242/// #   pub struct DocMockTransport {
243/// #       pub sent_messages: Arc<Mutex<VecDeque<(SipMessage, SocketAddr)>>>,
244/// #       pub event_injector: mpsc::Sender<TransportLayerEvent>,
245/// #       local_socket_addr: SocketAddr,
246/// #       is_transport_closed: Arc<Mutex<bool>>,
247/// #   }
248///
249/// #   impl DocMockTransport {
250/// #       pub fn new(event_injector: mpsc::Sender<TransportLayerEvent>, local_addr_str: &str) -> Self {
251/// #           DocMockTransport {
252/// #               sent_messages: Arc::new(Mutex::new(VecDeque::new())),
253/// #               event_injector,
254/// #               local_socket_addr: local_addr_str.parse().expect("Invalid local_addr_str for DocMockTransport"),
255/// #               is_transport_closed: Arc::new(Mutex::new(false)),
256/// #           }
257/// #       }
258/// #       pub async fn inject_event(&self, event: TransportLayerEvent) -> std::result::Result<(), String> {
259/// #           self.event_injector.send(event).await.map_err(|e| e.to_string())
260/// #       }
261/// #   }
262///
263/// #   #[async_trait]
264/// #   impl Transport for DocMockTransport {
265/// #       fn local_addr(&self) -> std::result::Result<SocketAddr, TransportError> {
266/// #           Ok(self.local_socket_addr)
267/// #       }
268/// #
269/// #       async fn send_message(&self, message: SipMessage, destination: SocketAddr) -> std::result::Result<(), TransportError> {
270/// #           if *self.is_transport_closed.lock().await {
271/// #               return Err(TransportError::TransportClosed);
272/// #           }
273/// #           let mut sent = self.sent_messages.lock().await;
274/// #           sent.push_back((message, destination));
275/// #           Ok(())
276/// #       }
277/// #
278/// #       async fn close(&self) -> std::result::Result<(), TransportError> {
279/// #           let mut closed_status = self.is_transport_closed.lock().await;
280/// #           *closed_status = true;
281/// #           Ok(())
282/// #       }
283/// #
284/// #       fn is_closed(&self) -> bool {
285/// #           self.is_transport_closed.try_lock().map_or(false, |guard| *guard)
286/// #       }
287/// #   }
288///
289/// #   pub fn build_invite_request(from_domain: &str, to_domain: &str, local_contact_host: &str) -> std::result::Result<SipCoreRequest, Box<dyn std::error::Error>> {
290/// #       let from_uri_str = format!("sip:alice@{}", from_domain);
291/// #       let to_uri_str = format!("sip:bob@{}", to_domain);
292/// #       let contact_uri_str = format!("sip:alice@{}", local_contact_host);
293/// #
294/// #       let request = SimpleRequestBuilder::new(Method::Invite, &to_uri_str)?
295/// #           .from("Alice", &from_uri_str, Some("fromtagClient1"))
296/// #           .to("Bob", &to_uri_str, None)
297/// #           .call_id(&format!("callclient1-{}", Uuid::new_v4()))
298/// #           .cseq(1)
299/// #           .contact(&contact_uri_str, Some("Alice Contact"))
300/// #           .via(&format!("{}:5060", local_contact_host), "UDP", Some(&format!("z9hG4bK{}", Uuid::new_v4().simple())))
301/// #           .header(TypedHeader::ContentLength(ContentLengthHeaderType::new(0)))
302/// #           .build();
303/// #       Ok(request)
304/// #   }
305/// #
306/// #   pub fn build_trying_response(invite_req: &SipCoreRequest) -> std::result::Result<SipCoreResponse, Box<dyn std::error::Error>> {
307/// #       let response = SimpleResponseBuilder::response_from_request(invite_req, StatusCode::Trying, Some("Trying"))
308/// #           .header(TypedHeader::ContentLength(ContentLengthHeaderType::new(0)))
309/// #           .build();
310/// #       Ok(response)
311/// #   }
312/// # }
313/// # use doctest_helpers::*;
314/// use rvoip_transaction_core::{TransactionManager, TransactionEvent, TransactionKey};
315/// use rvoip_sip_core::{Method, Message as SipMessage, Request as SipCoreRequest, Response as SipCoreResponse, types::status::StatusCode};
316/// use rvoip_sip_transport::{TransportEvent as TransportLayerEvent, Transport};
317/// use std::net::SocketAddr;
318/// use std::sync::Arc;
319/// use tokio::sync::mpsc;
320/// use std::time::Duration;
321///
322/// #[tokio::main]
323/// async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
324///     let (event_injector_tx, event_injector_rx_for_manager) = mpsc::channel(100);
325///     let mock_client_addr = "127.0.0.1:5080";
326///     let mock_transport = Arc::new(DocMockTransport::new(event_injector_tx, mock_client_addr));
327///
328///     let (manager, mut client_events_rx) = TransactionManager::new(
329///         mock_transport.clone() as Arc<dyn Transport>,
330///         event_injector_rx_for_manager,
331///         Some(10)
332///     ).await.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
333///
334///     let invite_req = build_invite_request("client.com", "server.com", mock_client_addr)?;
335///     let destination_server_addr: SocketAddr = "127.0.0.1:5090".parse()?;
336///
337///     let tx_id = manager.create_client_transaction(invite_req.clone(), destination_server_addr)
338///         .await.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
339///     println!("Client transaction created with ID: {}", tx_id);
340///     
341///     // Send the request after creating the transaction
342///     manager.send_request(&tx_id).await.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
343///
344///     // Allow more time for the transaction manager to send the message
345///     tokio::time::sleep(Duration::from_millis(100)).await;
346///
347///     // Process any state change events before checking messages
348///     tokio::select! {
349///         Some(event) = client_events_rx.recv() => {
350///             match event {
351///                 TransactionEvent::StateChanged { transaction_id, previous_state, new_state } 
352///                     if transaction_id == tx_id => {
353///                     println!("Transaction state changed from {:?} to {:?}", previous_state, new_state);
354///                     // This is the expected StateChanged event, continue with the test
355///                 },
356///                 _ => {} // Ignore other events
357///             }
358///         },
359///         _ = tokio::time::sleep(Duration::from_millis(50)) => {
360///             // Timeout waiting for event, continue anyway
361///         }
362///     }
363///
364///     let sent_messages = mock_transport.sent_messages.lock().await;
365///     assert_eq!(sent_messages.len(), 1, "INVITE should have been sent");
366///     if let Some((msg, dest)) = sent_messages.front() {
367///         assert!(msg.is_request(), "Sent message should be a request");
368///         assert_eq!(msg.method(), Some(Method::Invite));
369///         assert_eq!(*dest, destination_server_addr);
370///     } else {
371///         panic!("No message found in sent_messages");
372///     }
373///     drop(sent_messages);
374///
375///     let trying_response_msg = build_trying_response(&invite_req)?;
376///     mock_transport.inject_event(TransportLayerEvent::MessageReceived {
377///         message: SipMessage::Response(trying_response_msg.clone()),
378///         source: destination_server_addr,
379///         destination: mock_transport.local_addr()?,
380///     }).await?;
381///
382///     tokio::select! {
383///         Some(event) = client_events_rx.recv() => {
384///             match event {
385///                 TransactionEvent::ProvisionalResponse { transaction_id, response, .. } if transaction_id == tx_id => {
386///                     println!("Received Provisional Response: {} {}", response.status_code(), response.reason_phrase());
387///                     assert_eq!(response.status_code(), StatusCode::Trying.as_u16());
388///                 },
389///                 TransactionEvent::TransportError { transaction_id, .. } if transaction_id == tx_id => {
390///                     eprintln!("Transport error for transaction {}", transaction_id);
391///                     return Err("Transport error".into());
392///                 },
393///                 TransactionEvent::TransactionTimeout { transaction_id, .. } if transaction_id == tx_id => {
394///                     eprintln!("Transaction {} timed out", transaction_id);
395///                     return Err("Transaction timeout".into());
396///                 },
397///                 other_event => {
398///                     eprintln!("Received unexpected event: {:?}", other_event);
399///                     return Err("Unexpected event".into());
400///                 }
401///             }
402///         },
403///         _ = tokio::time::sleep(Duration::from_secs(2)) => {
404///             eprintln!("Timeout waiting for transaction event");
405///             return Err("Timeout waiting for event".into());
406///         }
407///     }
408///
409///     manager.shutdown().await;
410///     Ok(())
411/// }
412/// ```
413///
414/// ### 2. Basic Server Transaction
415///
416/// ```
417/// use rvoip_transaction_core::{TransactionManager, TransactionEvent};
418/// use rvoip_transaction_core::builders::{client_quick, server_quick};
419/// use rvoip_transaction_core::transport::{TransportManager, TransportManagerConfig};
420/// use rvoip_sip_core::{Method, StatusCode};
421/// use std::net::SocketAddr;
422/// use std::time::Duration;
423///
424/// #[tokio::main]
425/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
426///     // Create server transport
427///     let server_config = TransportManagerConfig {
428///         enable_udp: true,
429///         bind_addresses: vec!["127.0.0.1:5060".parse()?],
430///         ..Default::default()
431///     };
432///     
433///     let (mut server_transport, server_transport_rx) = TransportManager::new(server_config).await?;
434///     server_transport.initialize().await?;
435///     let server_addr = server_transport.default_transport().await
436///         .ok_or("No default transport")?.local_addr()?;
437///     
438///     // Create transaction manager
439///     let (server_tm, mut server_events) = TransactionManager::with_transport_manager(
440///         server_transport,
441///         server_transport_rx,
442///         Some(10),
443///     ).await?;
444///     
445///     // Clone for use in spawn (to avoid ownership issues)
446///     let server_tm_clone = server_tm.clone();
447///     
448///     // Handle incoming requests
449///     tokio::spawn(async move {
450///         while let Some(event) = server_events.recv().await {
451///             match event {
452///                 TransactionEvent::NonInviteRequest { transaction_id, request, source, .. } => {
453///                     println!("Received {} from {}", request.method(), source);
454///                     
455///                     // Send appropriate response using builders
456///                     match request.method() {
457///                         Method::Register => {
458///                             let ok = server_quick::ok_register(
459///                                 &request, 
460///                                 3600, 
461///                                 vec![format!("sip:user@{}", source.ip())]
462///                             ).expect("Failed to create REGISTER response");
463///                             
464///                             let _ = server_tm_clone.send_response(&transaction_id, ok).await;
465///                         },
466///                         Method::Options => {
467///                             let ok = server_quick::ok_options(
468///                                 &request, 
469///                                 vec![Method::Invite, Method::Register, Method::Options]
470///                             ).expect("Failed to create OPTIONS response");
471///                             
472///                             let _ = server_tm_clone.send_response(&transaction_id, ok).await;
473///                         },
474///                         _ => {
475///                             let ok = server_quick::ok_bye(&request)
476///                                 .expect("Failed to create OK response");
477///                             let _ = server_tm_clone.send_response(&transaction_id, ok).await;
478///                         }
479///                     }
480///                 },
481///                 TransactionEvent::InviteRequest { transaction_id, request, source, .. } => {
482///                     println!("Received INVITE from {}", source);
483///                     
484///                     // Send INVITE response
485///                     let ok = server_quick::ok_invite(
486///                         &request, 
487///                         Some("v=0\r\no=server 456 789 IN IP4 127.0.0.1\r\n...".to_string()),
488///                         format!("sip:server@{}", source.ip())
489///                     ).expect("Failed to create INVITE response");
490///                     
491///                     let _ = server_tm_clone.send_response(&transaction_id, ok).await;
492///                 },
493///                 _ => {}
494///             }
495///         }
496///     });
497///     
498///     // Keep server running for a bit
499///     tokio::time::sleep(Duration::from_millis(100)).await;
500///     server_tm.shutdown().await;
501///     Ok(())
502/// }
503/// ```
504///
505/// ### 3. Client INVITE with ACK Handling
506///
507/// This example demonstrates a client INVITE transaction, receiving a 2xx response,
508/// and then the Transaction User (TU) constructing and sending an ACK.
509///
510/// ```
511/// use rvoip_transaction_core::{TransactionManager, TransactionEvent};
512/// use rvoip_transaction_core::builders::client_quick;
513/// use rvoip_transaction_core::transport::{TransportManager, TransportManagerConfig};
514/// use rvoip_sip_core::{Method, StatusCode};
515/// use std::net::SocketAddr;
516/// use std::time::Duration;
517///
518/// #[tokio::main]
519/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
520///     // Create client transport
521///     let client_config = TransportManagerConfig {
522///         enable_udp: true,
523///         bind_addresses: vec!["127.0.0.1:0".parse()?], // Ephemeral port
524///         ..Default::default()
525///     };
526///     
527///     let (mut client_transport, client_transport_rx) = TransportManager::new(client_config).await?;
528///     client_transport.initialize().await?;
529///     let client_addr = client_transport.default_transport().await
530///         .ok_or("No default transport")?.local_addr()?;
531///     
532///     // Create transaction manager
533///     let (client_tm, mut client_events) = TransactionManager::with_transport_manager(
534///         client_transport,
535///         client_transport_rx,
536///         Some(10),
537///     ).await?;
538///     
539///     // Create INVITE using builders
540///     let server_addr: SocketAddr = "127.0.0.1:5060".parse()?;
541///     let invite_request = client_quick::invite(
542///         "sip:alice@example.com",
543///         "sip:bob@example.com", 
544///         client_addr,
545///         Some("v=0\r\no=alice 123 456 IN IP4 127.0.0.1\r\n")
546///     ).expect("Failed to create INVITE");
547///     
548///     // Create client transaction
549///     let tx_id = client_tm.create_client_transaction(invite_request, server_addr).await?;
550///     println!("Created INVITE transaction: {}", tx_id);
551///     
552///     // Send the INVITE
553///     client_tm.send_request(&tx_id).await?;
554///     println!("Sent INVITE request");
555///     
556///     // Handle events
557///     tokio::spawn(async move {
558///         while let Some(event) = client_events.recv().await {
559///             match event {
560///                 TransactionEvent::SuccessResponse { transaction_id, response, .. } => {
561///                     println!("Received {} {}", response.status_code(), response.reason_phrase());
562///                     
563///                     // For 2xx responses to INVITE, the TU must send ACK
564///                     // (This would normally be done by the dialog layer)
565///                     if response.status_code() >= 200 && response.status_code() < 300 {
566///                         println!("Would send ACK for 2xx response (handled by TU/Dialog layer)");
567///                     }
568///                 },
569///                 TransactionEvent::FailureResponse { transaction_id, response } => {
570///                     println!("Received error: {} {}", response.status_code(), response.reason_phrase());
571///                     // ACK for non-2xx responses is handled automatically by transaction layer
572///                 },
573///                 TransactionEvent::StateChanged { transaction_id, new_state, .. } => {
574///                     println!("Transaction {} state: {:?}", transaction_id, new_state);
575///                 },
576///                 _ => {}
577///             }
578///         }
579///     });
580///     
581///     // Keep client running for a bit
582///     tokio::time::sleep(Duration::from_millis(100)).await;
583///     client_tm.shutdown().await;
584///     Ok(())
585/// }
586/// ```
587///
588/// ## Transactional vs. Non-Transactional Behavior
589///
590/// SIP distinguishes between:
591///
592/// 1. **Transactional Messages**: Initial requests and their responses
593///    - Handled by this transaction layer
594///    - Examples: REGISTER, INVITE, BYE, etc.
595///
596/// 2. **Non-Transactional Messages**: Messages that don't create a transaction
597///    - ACK for 2xx responses (separate from the INVITE transaction)
598///    - CANCEL (creates its own transaction but refers to another)
599///    - In-dialog requests (handled at the dialog layer, but still use transactions)
600///
601/// ## RFC 3261 Compliance
602///
603/// This implementation follows the transaction state machines defined in RFC 3261:
604///
605/// * Section 17.1.1: INVITE client transactions
606/// * Section 17.1.2: Non-INVITE client transactions
607/// * Section 17.2.1: INVITE server transactions
608/// * Section 17.2.2: Non-INVITE server transactions
609///
610/// ## Error Handling
611///
612/// The library provides a comprehensive error system via the [`Error`] type, enabling
613/// detailed error information propagation for various failure scenarios. The transaction
614/// layer focuses on handling protocol-level errors such as timeouts, transport failures,
615/// and improper message sequences.
616/// # Example
617///
618/// ```
619/// use rvoip_transaction_core::prelude::*;
620/// use rvoip_transaction_core::transaction::AtomicTransactionState;
621/// use std::time::Duration;
622/// ```
623pub mod prelude {
624    pub use crate::transaction::{TransactionKey, TransactionEvent, TransactionState, TransactionKind};
625    pub use crate::manager::TransactionManager;
626    pub use rvoip_sip_transport::transport::TransportType;
627    pub use crate::transport::{
628        TransportCapabilities, TransportInfo,
629        NetworkInfoForSdp, WebSocketStatus, TransportCapabilitiesExt
630    };
631    pub use crate::error::{Error, Result};
632}
633
634#[cfg(test)]
635mod tests {
636    use super::*;
637    use crate::transaction::AtomicTransactionState;
638    use std::net::SocketAddr;
639    use std::str::FromStr;
640    use std::sync::Arc;
641    use tokio::sync::mpsc;
642    use std::time::Duration;
643
644    /// Mock Transport implementation for testing
645    #[derive(Debug)]
646    struct MockTransport {
647        local_addr: SocketAddr,
648    }
649
650    impl MockTransport {
651        fn new(addr: &str) -> Self {
652            Self {
653                local_addr: SocketAddr::from_str(addr).unwrap(),
654            }
655        }
656    }
657
658    #[async_trait::async_trait]
659    impl rvoip_sip_transport::Transport for MockTransport {
660        async fn send_message(
661            &self,
662            _message: rvoip_sip_core::Message,
663            _destination: SocketAddr,
664        ) -> std::result::Result<(), rvoip_sip_transport::error::Error> {
665            Ok(()) // Just pretend we sent it
666        }
667
668        fn local_addr(&self) -> std::result::Result<SocketAddr, rvoip_sip_transport::error::Error> {
669            Ok(self.local_addr)
670        }
671
672        async fn close(&self) -> std::result::Result<(), rvoip_sip_transport::error::Error> {
673            Ok(())
674        }
675
676        fn is_closed(&self) -> bool {
677            false
678        }
679    }
680
681    #[tokio::test]
682    async fn test_transaction_manager_creation() {
683        let transport = Arc::new(MockTransport::new("127.0.0.1:5060"));
684        let (_, transport_rx) = mpsc::channel(10);
685        
686        let result = TransactionManager::new(
687            transport,
688            transport_rx,
689            Some(100)
690        ).await;
691        
692        assert!(result.is_ok(), "Should create TransactionManager without error");
693    }
694
695    #[tokio::test]
696    async fn test_transaction_key_creation() {
697        let key = TransactionKey::new(
698            "z9hG4bK1234".to_string(),
699            rvoip_sip_core::Method::Invite,
700            false
701        );
702        
703        assert_eq!(key.branch, "z9hG4bK1234");
704        assert_eq!(key.method, rvoip_sip_core::Method::Invite);
705        assert_eq!(key.is_server, false);
706    }
707
708    #[tokio::test]
709    async fn test_transaction_state_transitions() {
710        // Instead of comparing with <, verify the state machine flow by checking
711        // that states are different and represent the correct sequence
712        let initial = TransactionState::Initial;
713        let calling = TransactionState::Calling;
714        let proceeding = TransactionState::Proceeding;
715        let completed = TransactionState::Completed;
716        let terminated = TransactionState::Terminated;
717        
718        // Verify states are different
719        assert_ne!(initial, calling);
720        assert_ne!(calling, proceeding);
721        assert_ne!(proceeding, completed);
722        assert_ne!(completed, terminated);
723        
724        // Verify some valid transitions for InviteClient
725        assert!(AtomicTransactionState::validate_transition(TransactionKind::InviteClient, initial, calling).is_ok());
726        assert!(AtomicTransactionState::validate_transition(TransactionKind::InviteClient, calling, proceeding).is_ok());
727        assert!(AtomicTransactionState::validate_transition(TransactionKind::InviteClient, proceeding, completed).is_ok());
728        assert!(AtomicTransactionState::validate_transition(TransactionKind::InviteClient, completed, terminated).is_ok());
729    }
730
731    #[tokio::test]
732    async fn test_timer_settings() {
733        // Test the timer settings defaults
734        let settings = TimerSettings::default();
735        
736        assert_eq!(settings.t1, Duration::from_millis(500), "T1 should be 500ms");
737        assert_eq!(settings.t2, Duration::from_secs(4), "T2 should be 4s");
738        
739        // Create a custom timer settings
740        let custom_settings = TimerSettings {
741            t1: Duration::from_millis(200),
742            t2: Duration::from_secs(2),
743            t4: Duration::from_secs(5),
744            timer_100_interval: Duration::from_millis(200),
745            transaction_timeout: Duration::from_secs(16),
746            wait_time_d: Duration::from_secs(16),
747            wait_time_h: Duration::from_secs(16),
748            wait_time_i: Duration::from_secs(2),
749            wait_time_j: Duration::from_secs(16),
750            wait_time_k: Duration::from_secs(2),
751        };
752        
753        assert_eq!(custom_settings.t1, Duration::from_millis(200));
754        assert_eq!(custom_settings.transaction_timeout, Duration::from_secs(16));
755    }
756}