### Test TLS start spawns read loop for existing connections Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/transport/tls.rs.html Initializes tracing and crypto provider, then generates test certificates. This is the beginning of a test that likely uses the certificates for TLS setup. ```rust init_tracing(); init_crypto_provider(); let (cert_pem, key_pem) = generate_test_certs(); ``` -------------------------------- ### Quick start: create a CallManager and outbound call Source: https://docs.rs/rsiprtp/latest/index.html Minimal setup for creating a call manager and an outbound call. Requires the tokio runtime and the rsiprtp prelude. The call ID is returned but event handling is left as a placeholder. ```rust use rsiprtp::prelude::*; #[tokio::main] async fn main() -> Result<(), Error> { // Create a call manager let config = ManagerConfig::default(); let mut manager = CallManager::new(config); // Create an outbound call let call_id = manager.create_call("sip:bob@example.com".to_string()); // ... handle call events ... Ok(()) } ``` -------------------------------- ### Test TURN client create_permission without allocation setup Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/ice/turn.rs.html Sets up a TURN client without an allocation and defines a peer address. Used as the setup for testing create_permission when no allocation exists. ```rust let mock = MockTurnServer::new().await; let server = TurnServer::new(mock.addr, "testuser", "testpass"); let mut client = TurnClient::new(server).await.unwrap(); let peer_addr: SocketAddr = "10.0.0.100:5060".parse().unwrap(); ``` -------------------------------- ### Setup for test_timer_i_timeout_invite_server Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/transaction/manager.rs.html Creates a TransactionManager, sends an INVITE request, polls actions, and obtains a handle for the server transaction. Used as setup for testing timer I timeout on an invite server. ```rust let mut mgr = TransactionManager::new(false); let invite = create_invite(); mgr.handle_message(SipMessage::Request(invite.clone())); let _ = mgr.poll_actions(); let handle = TransactionHandle(1); ``` -------------------------------- ### Test: TCP receiver dropped (setup) Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/transport/tcp.rs.html Starts a TCP server and obtains the receiver channel. The test is incomplete in the provided source, but it sets up the scenario for testing what happens when the receiver is dropped. ```rust #[tokio::test] async fn test_tcp_receiver_dropped() { // Create server let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0); let server = TcpTransport::bind(server_addr).await.unwrap(); let server_addr = server.local_addr(); let (rx, _sender) = server.start(); ``` -------------------------------- ### start Source: https://docs.rs/rsiprtp/latest/rsiprtp/transport/struct.TlsTransport.html Start the transport, accepting connections and receiving messages. Returns a receiver for incoming messages. ```APIDOC ## start ### Description Start the transport, accepting connections and receiving messages. Returns a receiver for incoming messages. ### Method start ### Returns (Receiver, TlsSender) - A receiver for incoming messages and a sender handle. ``` -------------------------------- ### or method example Source: https://docs.rs/rsiprtp/latest/rsiprtp/core/error/type.Result.html Shows how or returns the Ok value of self if self is Ok, otherwise returns the provided res. The example covers all four combinations of Ok and Err for self and res. ```rust let x: Result = Ok(2); let y: Result = Err("late error"); assert_eq!(x.or(y), Ok(2)); let x: Result = Err("early error"); let y: Result = Ok(2); assert_eq!(x.or(y), Ok(2)); let x: Result = Err("not a 2"); let y: Result = Err("late error"); assert_eq!(x.or(y), Err("late error")); let x: Result = Ok(2); let y: Result = Ok(100); assert_eq!(x.or(y), Ok(2)); ``` -------------------------------- ### and_then example function Source: https://docs.rs/rsiprtp/latest/rsiprtp/core/error/type.Result.html Shows a function that uses `checked_mul` and `ok_or` to return a `Result`, which can be used with `and_then` for control flow. The example is from the `and_then` method documentation. ```rust fn sq_then_to_string(x: u32) -> Result { x.checked_mul(x).map(|sq| sq.to_string()).ok_or("overflowed") } ``` -------------------------------- ### init_crypto_provider: Install ring crypto provider Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/transport/tls.rs.html Installs the ring crypto provider for tests, using a static Once to ensure it runs only once. Required for rustls operations in tests. ```rust fn init_crypto_provider() { CRYPTO_INIT.call_once(|| { let _ = rustls::crypto::ring::default_provider().install_default(); }); } ``` -------------------------------- ### Test gather_srflx_candidates setup with mock STUN server Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/ice/agent.rs.html Sets up a mock STUN server and an IceAgent with that server, then gathers host candidates before spawning the mock server to respond. This is the setup for testing srflx candidate gathering. ```rust init_tracing(); // Create mock STUN server let mock = MockStunServer::new().await; let config = IceConfig { stun_servers: vec![StunServer { name: "mock", addr: mock.addr, }], ..Default::default() }; let agent = IceAgent::new(config, IceRole::Controlling); // First gather host candidates let _ = agent.gather_host_candidates().await; // Spawn mock server to respond (respond once per request) let mock_socket = mock.socket.clone(); ``` -------------------------------- ### Test fallback_prefers_host_to_host setup Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/ice/agent.rs.html Sets up an IceAgent with no STUN servers, adds a server-reflexive local candidate and a host local candidate, then adds a host remote candidate. This is the setup for testing that fallback selection prefers host-to-host pairs. ```rust let config = IceConfig { stun_servers: vec![], ..Default::default() }; let agent = IceAgent::new(config, IceRole::Controlling); // Add srflx local candidate let local_srflx = Candidate::server_reflexive( SocketAddr::new(IpAddr::V4(std::net::Ipv4Addr::new(1, 2, 3, 4)), 5001), SocketAddr::new(IpAddr::V4(std::net::Ipv4Addr::new(192, 168, 1, 1)), 5001), 1, ); // Add host local candidate let local_host = Candidate::host( SocketAddr::new(IpAddr::V4(std::net::Ipv4Addr::new(192, 168, 1, 2)), 5002), 1, ); agent.local_candidates.write().await.push(local_srflx); agent.local_candidates.write().await.push(local_host); // Add host remote candidate let remote_host = Candidate::host( SocketAddr::new(IpAddr::V4(std::net::Ipv4Addr::new(192, 168, 1, 100)), 5000), ``` -------------------------------- ### ICE candidate gathering with timeout and dispatcher setup Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/ice/agent.rs.html These functions implement the ICE candidate gathering flow: the outer method sets the state to Gathering and runs the inner gathering with a timeout, returning partial candidates on timeout. The inner method gathers host candidates first, stashes them, then gathers server-reflexive candidates, storing the final set. Host gathering binds a UDP socket per address, creates a host candidate, and starts a dispatcher task via ensure_dispatcher, which spawns a stun_dispatcher_loop for each socket if not already running. ```rust *self.state.write().await = IceState::Gathering; info!("Starting ICE candidate gathering"); let budget = std::time::Duration::from_millis(self.config.gather_timeout_ms); match tokio::time::timeout(budget, self.gather_candidates_inner()).await { Ok(result) => result, Err(_) => { // Timeout: return whatever was stored so far. let partial = self.local_candidates.read().await.clone(); warn!( "ICE candidate gathering timed out after {}ms; returning {} partial candidates", self.config.gather_timeout_ms, partial.len() ); Ok(partial) } } } ``` ```rust async fn gather_candidates_inner(&self) -> Result, IceError> { let mut candidates = Vec::new(); // Gather host candidates from local interfaces let host_candidates = self.gather_host_candidates().await?; candidates.extend(host_candidates); // Stash hosts before we start STUN — if srflx gathering times out // mid-flight, the timeout branch can still return the hosts. *self.local_candidates.write().await = candidates.clone(); // Gather server-reflexive candidates from STUN let srflx_candidates = self.gather_srflx_candidates().await; candidates.extend(srflx_candidates); // Store final candidate set *self.local_candidates.write().await = candidates.clone(); info!("Gathered {} candidates", candidates.len()); Ok(candidates) } ``` ```rust async fn gather_host_candidates(&self) -> Result, IceError> { let addrs = get_local_addresses(); self.gather_host_candidates_with(addrs).await } ``` ```rust async fn gather_host_candidates_with( &self, addrs: Vec, ) -> Result, IceError> { let mut candidates = Vec::new(); for addr in addrs { // Bind a socket for this address let bind_addr = SocketAddr::new(addr, 0); match UdpSocket::bind(bind_addr).await { Ok(socket) => { let local_addr = socket_local_addr(&socket)?; debug!("Bound socket to {}", local_addr); // Create host candidate let candidate = Candidate::host(local_addr, 1); candidates.push(candidate); // Store socket and start a dispatcher task for it. let socket = Arc::new(socket); self.sockets .write() .await .insert(local_addr, socket.clone()); self.ensure_dispatcher(local_addr, socket).await; } Err(e) => { warn!("Failed to bind to {}: {}", bind_addr, e); } } } Ok(candidates) } ``` ```rust async fn ensure_dispatcher(&self, local_addr: SocketAddr, socket: Arc) { let mut tasks = self.responder_tasks.write().await; if tasks.contains_key(&local_addr) { return; } let pending = self.pending_responses.clone(); let local_ufrag = self.config.local_ufrag.clone(); let local_pwd = self.config.local_pwd.clone(); let handle = tokio::spawn(async move { stun_dispatcher_loop(socket, pending, local_ufrag, local_pwd).await; }); tasks.insert(local_addr, handle); } ``` -------------------------------- ### Setup TLS client-server for tests Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/transport/tls.rs.html Sets up a TLS client and server for testing. It creates temporary certificate and key files, binds a TlsTransport server, and connects a client with verification disabled. Returns the server receiver, client, and server address. ```rust async fn setup_tls_client_server() -> (mpsc::Receiver, TlsTransport, SocketAddr) { use std::io::Write; use tempfile::NamedTempFile; init_crypto_provider(); let (cert_pem, key_pem) = generate_test_certs(); let mut cert_file = NamedTempFile::new().unwrap(); cert_file.write_all(&cert_pem).unwrap(); cert_file.flush().unwrap(); let mut key_file = NamedTempFile::new().unwrap(); key_file.write_all(&key_pem).unwrap(); key_file.flush().unwrap(); let server_config = TlsServerConfig { cert_path: cert_file.path().to_str().unwrap().to_string(), key_path: key_file.path().to_str().unwrap().to_string(), }; let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0); let server = TlsTransport::bind_server(server_addr, server_config) .await .unwrap(); let server_addr = server.local_addr(); let (server_rx, _sender) = server.start(); let client_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0); let client_config = TlsClientConfig { verify_server: false, ca_cert_path: None, }; let client = TlsTransport::new_client(client_addr, client_config).unwrap(); client.connect(server_addr, "localhost").await.unwrap(); (server_rx, client, server_addr) } ``` -------------------------------- ### Test build refresh request setup Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/ice/turn.rs.html Starts the test for building a REFRESH request by generating a transaction ID and setting a lifetime value of 600 seconds. ```rust let transaction_id = generate_transaction_id(); let lifetime = 600u32; ``` -------------------------------- ### Build SDP offer and parse it Source: https://docs.rs/rsiprtp/latest/rsiprtp/sdp/index.html Shows how to create an SDP offer with SdpBuilder, add audio media with PCMU and PCMA codecs, convert to string, and parse it back using SessionDescription::parse. Use this as a starting point for constructing and parsing SDP messages. ```rust use rsiprtp::sdp::{SessionDescription, SdpBuilder, MediaBuilder, Codec, create_answer}; use std::net::{IpAddr, Ipv4Addr}; // Build an SDP offer let addr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)); let offer = SdpBuilder::new(addr) .add_media(MediaBuilder::audio(49170).pcmu().pcma()) .build(); // Parse SDP from string let sdp_str = offer.to_string(); let parsed = SessionDescription::parse(&sdp_str).unwrap(); ``` -------------------------------- ### Example: Resolving a SIP URI with SipResolver Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/transport/resolver.rs.html Shows how to create a SipResolver and resolve a domain to a list of targets. Requires the tokio runtime and the rsiprtp crate. ```rust use rsiprtp::transport::{SipResolver, ResolvedTarget}; #[tokio::main] async fn main() -> Result<(), Box> { let resolver = SipResolver::new().await?; // Resolve a SIP URI let targets = resolver.resolve("example.com", None).await?; for target in targets { println!("{}:{} via {:?}", target.host, target.port, target.transport); } Ok(()) } ``` -------------------------------- ### TurnServer::new Source: https://docs.rs/rsiprtp/latest/rsiprtp/ice/struct.TurnServer.html Creates a new TURN server configuration with the given address, username, and password. ```APIDOC ## TurnServer::new ### Description Creates a new TURN server configuration with the given address, username, and password. ### Method Constructor ### Signature ```rust pub fn new(addr: SocketAddr, username: &str, password: &str) -> Self ``` ### Parameters - **addr** (`SocketAddr`) - Required - Server address. - **username** (`&str`) - Required - Username for authentication. - **password** (`&str`) - Required - Password/credential for authentication. ### Returns - `Self` - A new `TurnServer` instance. ``` -------------------------------- ### as_mut: mutable references to Result contents Source: https://docs.rs/rsiprtp/latest/rsiprtp/core/error/type.Result.html Use to get mutable references to the contained Ok or Err values, allowing in-place modification. The example defines a helper function that mutates the inner value and then verifies the changes. ```rust fn mutate(r: &mut Result) { match r.as_mut() { Ok(v) => *v = 42, Err(e) => *e = 0, } } let mut x: Result = Ok(2); mutate(&mut x); assert_eq!(x.unwrap(), 42); let mut x: Result = Err(13); mutate(&mut x); assert_eq!(x.unwrap_err(), 0); ``` -------------------------------- ### Test TcpTransport start with existing connections Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/transport/tcp.rs.html Tests that TcpTransport can start with pre-existing connections. Establishes a connection before calling start(), then verifies the connection is tracked and can still send/receive messages after start. ```rust let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0); let server = TcpTransport::bind(server_addr).await.unwrap(); let server_addr = server.local_addr(); let (mut server_rx, _server_sender) = server.start(); // Create client and establish connection BEFORE starting let client_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0); let client = TcpTransport::bind(client_addr).await.unwrap(); // Connect before starting (this creates an existing connection) client.connect(server_addr).await.unwrap(); // Verify connection exists { let connections = client.connections.read().await; assert_eq!(connections.len(), 1); } // Now start the client transport with existing connection let (_client_rx, _client_sender) = client.start(); // The existing connection should be able to send messages let new_client = TcpTransport::bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0)) .await .unwrap(); let msg = b"INVITE sip:test@example.com SIP/2.0\r\nContent-Length: 0\r\n\r\n"; new_client.send_to(msg, server_addr).await.unwrap(); // Server should receive it let received = tokio::time::timeout(std::time::Duration::from_secs(1), server_rx.recv()) .await .unwrap() .unwrap(); assert_eq!(&received.data[..], msg); ``` -------------------------------- ### test_tcp_start_client_only - client-only transport start Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/transport/tcp.rs.html Verifies that TcpTransport::new_client can start without a listener. The start method returns a receiver and sender. ```rust #[tokio::test] async fn test_tcp_start_client_only() { // Create client-only transport let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5060); let transport = TcpTransport::new_client(addr); // Start should work even without listener let (_rx, _sender) = transport.start(); } ``` -------------------------------- ### handle_incoming_invite(&mut self, dialog: Dialog, offer_sdp: &SessionDescription) -> Option<(CallId, SessionDescription, u16)> Source: https://docs.rs/rsiprtp/latest/rsiprtp/session/struct.CallManager.html Accepts an incoming INVITE and creates a call. Returns the call ID and the SDP answer to send in 200 OK. ```APIDOC ## handle_incoming_invite(&mut self, dialog: Dialog, offer_sdp: &SessionDescription) -> Option<(CallId, SessionDescription, u16)> ### Description Accept an incoming INVITE and create a call. Returns the call ID and the SDP answer to send in 200 OK. ### Parameters - **dialog** (Dialog) - Required - The dialog associated with the incoming INVITE. - **offer_sdp** (&SessionDescription) - Required - The SDP offer from the INVITE. ### Returns - **Option<(CallId, SessionDescription, u16)>** - A tuple containing the call ID, the SDP answer, and the RTP port, or None if codec negotiation fails. ``` -------------------------------- ### Test handle_incoming_invite with no compatible media Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/session/manager.rs.html Tests handle_incoming_invite with a video-only SDP. Expects None and no call created, confirming no compatible media. ```rust let mut manager = CallManager::new(ManagerConfig::default()); let dialog = Dialog::new_uas( "call-124".to_string(), "from-tag".to_string(), "to-tag".to_string(), "sip:alice@example.com".to_string(), "sip:bob@example.com".to_string(), 1, ); let offer_sdp = test_video_only_sdp(); let result = manager.handle_incoming_invite(dialog, &offer_sdp); assert!(result.is_none()); assert_eq!(manager.call_count(), 0); ``` -------------------------------- ### Test find_header_end at start Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/transport/tls.rs.html Tests find_header_end when the header terminator is at the start; expects Some(4). ```rust #[test] fn test_find_header_end_at_start() { let data = b"\r\n\r\nSome content here"; assert_eq!(find_header_end(data), Some(4)); } ``` -------------------------------- ### test_tcp_connection_buffer_initialization: setup TcpListener and connect Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/transport/tcp.rs.html Test that TcpConnection initializes with the correct buffer capacity. It binds a TcpListener and spawns a task to connect a TcpStream, setting up the connection for buffer initialization checks. ```rust let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); let addr = listener.local_addr().unwrap(); let connect_handle = tokio::spawn(async move { TcpStream::connect(addr).await.unwrap() }); ``` -------------------------------- ### Test parse_content_length at start Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/transport/tls.rs.html Verifies that parse_content_length correctly parses a Content-Length header when it appears at the start of the headers. ```rust #[test] fn test_parse_content_length_at_start() { let headers = b"Content-Length: 50\r\nVia: test\r\n\r\n"; assert_eq!(parse_content_length(headers), 50); } ``` -------------------------------- ### create_answer(offer: &SessionDescription, local_codecs: &[Codec], local_port: u16) -> Option<(SessionDescription, Vec)> Source: https://docs.rs/rsiprtp/latest/rsiprtp/sdp/negotiation/fn.create_answer.html Negotiates an answer to an SDP offer. Returns the negotiated media descriptions or None if no compatible codec found. ```APIDOC ## Rust Function: create_answer ### Description Negotiate an answer to an SDP offer. Returns the negotiated media descriptions or None if no compatible codec found. ### Signature ```rust pub fn create_answer( offer: &SessionDescription, local_codecs: &[Codec], local_port: u16, ) -> Option<(SessionDescription, Vec)> ``` ### Parameters - **offer** (&SessionDescription) - The SDP offer to negotiate. - **local_codecs** (&[Codec]) - The list of local codecs available for negotiation. - **local_port** (u16) - The local port to use for the answer. ### Returns - **Option<(SessionDescription, Vec)>** - Returns the negotiated session description and a vector of negotiated media descriptions, or None if no compatible codec found. ``` -------------------------------- ### Test DtmfReceiver Start Clears Last Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/rtp/dtmf.rs.html Confirms that starting a new event clears the last_event, so a subsequent end for the same digit is recognized. ```rust let mut receiver = DtmfReceiver::new(101); // End an event let end_event = DtmfEvent::new(DtmfDigit::One, 500).with_end(); receiver.process_packet(101, &end_event.encode()); // Start a new event (clears last_event) let start_event = DtmfEvent::new(DtmfDigit::Two, 100); receiver.process_packet(101, &start_event.encode()); // Now same digit end should return (last_event was cleared by start) let end_event2 = DtmfEvent::new(DtmfDigit::One, 500).with_end(); let result = receiver.process_packet(101, &end_event2.encode()); assert_eq!(result, Some(DtmfDigit::One)); ``` -------------------------------- ### Clone Timing and assert start equality Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/sdp/parser.rs.html Clones the Timing field and asserts the cloned start equals the original. Used in the test_timing_clone test. ```rust let sdp = SessionDescription::parse(BASIC_SDP).unwrap(); let cloned = sdp.timing.clone(); assert_eq!(cloned.start, sdp.timing.start); ``` -------------------------------- ### Test display host only Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/sip/uri.rs.html Builds a URI with only a host and checks that the string representation is 'sip:example.com'. ```rust let uri = SipUri::builder().host("example.com").build().unwrap(); let s = uri.to_string(); assert_eq!(s, "sip:example.com"); ``` -------------------------------- ### Test allocate timeout setup Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/ice/turn.rs.html Sets up a mock TURN server and client for testing allocation timeout. ```rust let mock = MockTurnServer::new().await; let server = TurnServer::new(mock.addr, "testuser", "testpass"); let mut client = TurnClient::new(server).await.unwrap(); ``` -------------------------------- ### Test handle_incoming_invite with compatible media Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/session/manager.rs.html Tests handle_incoming_invite with a compatible SDP. Expects a successful result, one call created, direction Inbound, state Ringing, and an audio port >= 10000. ```rust let mut manager = CallManager::new(ManagerConfig::default()); let dialog = Dialog::new_uas( "call-123".to_string(), "from-tag".to_string(), "to-tag".to_string(), "sip:alice@example.com".to_string(), "sip:bob@example.com".to_string(), 1, ); let offer_sdp = test_sdp(); let result = manager.handle_incoming_invite(dialog, &offer_sdp); assert!(result.is_some()); let (call_id, answer_sdp, _port) = result.unwrap(); assert_eq!(manager.call_count(), 1); let call = manager.get_call(&call_id).unwrap(); assert_eq!(call.direction(), CallDirection::Inbound); assert_eq!(call.state(), CallState::Ringing); // Check answer SDP has correct port let audio = answer_sdp.audio_media().unwrap(); assert!(audio.port >= 10000); ``` -------------------------------- ### unwrap_or default example Source: https://docs.rs/rsiprtp/latest/rsiprtp/core/error/type.Result.html Shows how unwrap_or returns the contained Ok value or a provided default. The example uses a default of 2 and tests both Ok and Err cases. ```rust let default = 2; let x: Result = Ok(9); assert_eq!(x.unwrap_or(default), 9); let x: Result = Err("error"); assert_eq!(x.unwrap_or(default), default); ``` -------------------------------- ### Test handle_invite_success with no media Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/session/manager.rs.html Tests handle_invite_success with a video-only answer SDP. Expects false, indicating no compatible media. ```rust let mut manager = CallManager::new(ManagerConfig::default()); let call_id = manager.create_call("sip:bob@example.com".to_string()); let dialog = Dialog::new_uac( "call-123".to_string(), "from-tag".to_string(), "to-tag".to_string(), "sip:alice@example.com".to_string(), "sip:bob@example.com".to_string(), 1, ); let answer_sdp = test_video_only_sdp(); let result = manager.handle_invite_success(&call_id, dialog, &answer_sdp, None, Instant::now()); assert!(!result); ``` -------------------------------- ### Test TURN client create_permission success Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/ice/turn.rs.html Sets up a TURN allocation, spawns a mock server that responds to a permission request, and verifies that create_permission succeeds. The mock server reads the transaction ID from the request and sends a success response. ```rust let mock = MockTurnServer::new().await; let server = TurnServer::new(mock.addr, "testuser", "testpass"); let mut client = TurnClient::new(server).await.unwrap(); // Set up allocation manually client.allocation = Some(TurnAllocation { relayed_addr: "1.2.3.4:5000".parse().unwrap(), mapped_addr: "5.6.7.8:6000".parse().unwrap(), lifetime: 600, realm: "test.realm".to_string(), nonce: "testnonce".to_string(), }); let peer_addr: SocketAddr = "10.0.0.100:5060".parse().unwrap(); // Spawn mock server let mock_socket = mock.socket; tokio::spawn(async move { let mut buf = [0u8; 4096]; let (_, peer) = mock_socket.recv_from(&mut buf).await.unwrap(); let mut txn_id = [0u8; 12]; txn_id.copy_from_slice(&buf[8..20]); let response = MockTurnServer::build_permission_success(&txn_id); mock_socket.send_to(&response, peer).await.unwrap(); }); let result = client.create_permission(peer_addr).await; assert!(result.is_ok()); ``` -------------------------------- ### Reject empty start line after split_message Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/sip/parser/framing.rs.html Tests that `split_message` accepts a message consisting only of `\r\n\r\n`, but the resulting empty start line is rejected by both `parse_request_line` and `parse_status_line`. This confirms the layered rejection behavior. ```rust let res = split_message(b"\r\n\r\n"); // split_message itself accepts (separator at offset 0). assert!(res.is_ok(), "split_message accepts \r\n\r\n"); let (start, _hdr, _body) = res.unwrap(); assert!(start.is_empty(), "start line is empty"); // Both downstream start-line parsers reject empty input. assert!(parse_request_line(start).is_err()); assert!(parse_status_line(start).is_err()); ``` -------------------------------- ### Example using BitrateBridge::poll() Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/session/bitrate_bridge.rs.html Example from module docs showing how to instantiate BitrateBridge and call poll() with a target bitrate, checking if the codec was updated. Uses a `?` operator assuming a Result-returning context. ```rust use std::time::Instant; use rsiprtp::session::BitrateBridge; let mut bridge = BitrateBridge::new(); let applied = bridge.poll(cc.target_bitrate(), &mut codec, Instant::now())?; if applied { tracing::debug!("encoder bitrate updated"); } ``` -------------------------------- ### test_sdp_builder_multiple_media: add two media Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/sdp/builder.rs.html Verifies that adding two media via add_media results in two media entries. ```rust #[test] fn test_sdp_builder_multiple_media() { let addr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)); let sdp = SdpBuilder::new(addr) .add_media(MediaBuilder::audio(5000).pcmu()) .add_media(MediaBuilder::audio(5002).pcma()) .build(); assert_eq!(sdp.media.len(), 2); } ``` -------------------------------- ### Test receiver start and end events Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/rtp/dtmf.rs.html Tests that a start event without an end returns None and sets the current digit, while a subsequent end event returns the digit. Requires a DtmfReceiver instance. ```rust let start_event = DtmfEvent::new(DtmfDigit::Seven, 100); let result = receiver.process_packet(101, &start_event.encode()); assert!(result.is_none()); // Not complete yet assert_eq!(receiver.current_digit(), Some(DtmfDigit::Seven)); // End event let end_event = DtmfEvent::new(DtmfDigit::Seven, 500).with_end(); let result = receiver.process_packet(101, &end_event.encode()); assert_eq!(result, Some(DtmfDigit::Seven)); ``` -------------------------------- ### test_sdp_builder_audio_shorthand: add audio media Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/sdp/builder.rs.html Verifies that the audio shorthand adds one media with the given port. ```rust #[test] fn test_sdp_builder_audio_shorthand() { let addr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)); let sdp = SdpBuilder::new(addr).audio(49170).build(); assert_eq!(sdp.media.len(), 1); assert_eq!(sdp.media[0].port, 49170); } ``` -------------------------------- ### StunServer::new Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/ice/stun.rs.html Creates a custom STUN server with the given address and name. This is a synchronous constructor. ```APIDOC ## StunServer::new ### Description Creates a custom STUN server with the given address and name. This is a synchronous constructor. ### Method fn new(addr: SocketAddr, name: &'static str) -> Self ### Parameters - `addr` (SocketAddr) - Required - The server address. - `name` (&'static str) - Required - The server name for logging. ### Returns - `Self` - The new StunServer instance. ``` -------------------------------- ### and_then with Path metadata example Source: https://docs.rs/rsiprtp/latest/rsiprtp/core/error/type.Result.html Demonstrates using and_then with metadata() and modified() to chain fallible operations on a Path. The example checks that the root path succeeds and a bad path fails with ErrorKind::NotFound. ```rust use std::{io::ErrorKind, path::Path}; // Note: on Windows "/" maps to "C:\" let root_modified_time = Path::new("/").metadata().and_then(|md| md.modified()); assert!(root_modified_time.is_ok()); let should_fail = Path::new("/bad/path").metadata().and_then(|md| md.modified()); assert!(should_fail.is_err()); assert_eq!(should_fail.unwrap_err().kind(), ErrorKind::NotFound); ``` -------------------------------- ### ICE agent candidate gathering example Source: https://docs.rs/rsiprtp/latest/rsiprtp/ice/index.html Example usage of the ICE agent to gather local candidates. It creates an IceAgent with default configuration and the Controlling role, then calls gather_candidates() and prints the number of candidates. ```rust use rsiprtp::ice::{IceAgent, IceConfig, IceRole, StunServer}; #[tokio::main] async fn main() -> Result<(), Box> { // Create ICE agent let config = IceConfig::default(); let agent = IceAgent::new(config, IceRole::Controlling); // Gather local candidates let candidates = agent.gather_candidates().await?; println!("Gathered {} candidates", candidates.len()); Ok(()) } ``` -------------------------------- ### Create a CallManager and initiate an outbound call Source: https://docs.rs/rsiprtp/latest/rsiprtp/session/index.html Shows how to create a `CallManager` with default configuration and create an outbound call to a SIP URI. The `call_id` returned can be used to manage the call. ```rust use rsiprtp::session::{CallManager, ManagerConfig}; let config = ManagerConfig::default(); let mut manager = CallManager::new(config); // Create an outbound call let call_id = manager.create_call("sip:bob@example.com".to_string()); ``` -------------------------------- ### Add bandwidth to SDP media Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/sdp/builder.rs.html Builds an SDP with a single audio media and then inserts a bandwidth attribute (AS:64) into the media's bandwidth map. Verifies the output contains the expected 'b=AS:64' line. ```rust let addr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)); let mut sdp = SdpBuilder::new(addr) .add_media(MediaBuilder::audio(5000).pcmu()) .build(); // Add bandwidth sdp.media[0].bandwidth.insert("AS".to_string(), 64); let s = sdp.to_string(); assert!(s.contains("b=AS:64")); ``` -------------------------------- ### sender Source: https://docs.rs/rsiprtp/latest/rsiprtp/transport/struct.TlsTransport.html Get a sender handle. ```APIDOC ## sender ### Description Get a sender handle. ### Method sender ### Returns TlsSender - A sender handle. ``` -------------------------------- ### local_addr Source: https://docs.rs/rsiprtp/latest/rsiprtp/transport/struct.TlsTransport.html Get the local address. ```APIDOC ## local_addr ### Description Get the local address. ### Method local_addr ### Returns SocketAddr - The local address. ``` -------------------------------- ### codecs Source: https://docs.rs/rsiprtp/latest/rsiprtp/session/struct.CallManager.html Get the supported codecs. ```APIDOC ## codecs ### Description Get the supported codecs. ### Method pub fn ### Signature pub fn codecs(&self) -> &[Codec] ### Parameters None. ### Returns - **&[Codec]** - The slice of supported codecs. ``` -------------------------------- ### SDP module overview and re-exports Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/sdp/mod.rs.html Module-level documentation and re-exports for the SDP module. Shows how to build an SDP offer with SdpBuilder and MediaBuilder, parse it with SessionDescription::parse, and lists the public types re-exported from builder, negotiation, and parser submodules. ```rust //! SDP (Session Description Protocol) handling. //! //! Implements SDP parsing, building, and the offer/answer model per RFC 3264. //! //! # Overview //! //! This crate provides: //! - SDP parsing (`SessionDescription::parse`) //! - SDP building (`SdpBuilder`) //! - Offer/answer negotiation (`create_answer`, `process_answer`) //! //! # Example //! //! ``` //! use rsiprtp::sdp::{SessionDescription, SdpBuilder, MediaBuilder, Codec, create_answer}; //! use std::net::{IpAddr, Ipv4Addr}; //! //! // Build an SDP offer //! let addr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)); //! let offer = SdpBuilder::new(addr) //! .add_media(MediaBuilder::audio(49170).pcmu().pcma()) //! .build(); //! //! // Parse SDP from string //! let sdp_str = offer.to_string(); //! let parsed = SessionDescription::parse(&sdp_str).unwrap(); //! ``` pub mod builder; pub mod ice_attrs; pub mod negotiation; pub mod parser; #[cfg(test)] mod tests; // Re-export main types pub use builder::{MediaBuilder, SdpBuilder}; pub use negotiation::{create_answer, process_answer, Codec, NegotiatedMedia}; pub use parser::{ Attribute, Connection, Direction, Fmtp, MediaDescription, MediaType, Origin, RtpMap, SdpParseError, SessionDescription, Timing, }; ``` -------------------------------- ### RouteSet::routes Source: https://docs.rs/rsiprtp/latest/rsiprtp/sip/struct.RouteSet.html Gets the routes. ```APIDOC ## RouteSet::routes ### Description Gets the routes. ### Method routes ### Signature pub fn routes(&self) -> &[Route] ### Parameters None ### Returns &[Route] - A slice of the routes in the set. ``` -------------------------------- ### Create UAC dialog from INVITE Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/dialog/invite.rs.html Starts a UAC dialog from an INVITE, preparing for subsequent response handling. ```rust let invite = create_invite(); let mut dialog = InviteDialog::new_uac(invite.clone()); ``` -------------------------------- ### Start connectivity checks Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/ice/agent.rs.html Starts connectivity checks by setting the ICE state to Checking. Performs STUN connectivity checks on candidate pairs according to RFC 8445, but peer-reflexive discovery for symmetric NAT is not implemented. ```rust pub async fn start_checks(&self) -> Result<(), IceError> { *self.state.write().await = IceState::Checking; } ``` -------------------------------- ### Create two dialogs with SipRequest::builder and assert handles Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/dialog/manager.rs.html Creates two SIP INVITE requests with distinct call IDs and tags, then creates dialogs via the manager. Asserts that the returned handles differ and that both dialogs are retrievable. ```rust let invite1 = SipRequest::builder() .method(Method::Invite) .uri("sip:bob@example.com") .via("192.168.1.1", 5060, "UDP", "z9hG4bKtest1") .from("sip:alice@example.com", "fromtag1") .to("sip:bob@example.com") .call_id("call1@example.com") .cseq(1) .contact("sip:alice@192.168.1.1:5060") .build() .unwrap(); let handle1 = mgr.create_dialog(invite1).unwrap(); // Create second dialog let invite2 = SipRequest::builder() .method(Method::Invite) .uri("sip:carol@example.com") .via("192.168.1.1", 5060, "UDP", "z9hG4bKtest2") .from("sip:alice@example.com", "fromtag2") .to("sip:carol@example.com") .call_id("call2@example.com") .cseq(1) .contact("sip:alice@192.168.1.1:5060") .build() .unwrap(); let handle2 = mgr.create_dialog(invite2).unwrap(); assert_ne!(handle1, handle2); assert!(mgr.dialog(handle1).is_some()); assert!(mgr.dialog(handle2).is_some()); ``` -------------------------------- ### test_update_slow_start: Increase from slow start Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/rtp/session.rs.html Tests that update() increases target bitrate from slow start after waiting for the increase interval. Uses CongestionController::new() with initial bitrate 100_000, then sleeps 110ms before calling update(). ```rust let mut cc = CongestionController::new(100_000, 10_000, 1_000_000); // Wait for increase interval std::thread::sleep(Duration::from_millis(110)); cc.update(); // Should have increased from slow start assert!(cc.target_bitrate() > 100_000); ``` -------------------------------- ### SdpBuilder::build Source: https://docs.rs/rsiprtp/latest/rsiprtp/sdp/builder/struct.SdpBuilder.html Builds the SDP session description. ```APIDOC ## SdpBuilder::build ### Description Builds the SDP session description. ### Method build ### Parameters None ### Returns - **SessionDescription** - The built SDP session description. ``` -------------------------------- ### SdpBuilder::build_string Source: https://docs.rs/rsiprtp/latest/rsiprtp/sdp/builder/struct.SdpBuilder.html Builds and renders the SDP session description to a string. ```APIDOC ## SdpBuilder::build_string ### Description Builds and renders the SDP session description to a string. ### Method build_string ### Parameters None ### Returns - **String** - The SDP session description as a string. ``` -------------------------------- ### new Source: https://docs.rs/rsiprtp/latest/rsiprtp/transport/struct.SipResolver.html Creates a new SIP DNS resolver using the system DNS configuration. ```APIDOC ## new() ### Description Creates a new SIP DNS resolver using the system DNS configuration. ### Method new ### Signature `pub async fn new() -> Result` ### Parameters None ### Returns - `Result` - A new `SipResolver` instance or a `ResolverError` if creation fails. ``` -------------------------------- ### or_else control flow example Source: https://docs.rs/rsiprtp/latest/rsiprtp/core/error/type.Result.html Demonstrates or_else, which calls the provided closure only when the result is Err. The example defines sq and err functions and shows chaining or_else calls to control flow based on result values. ```rust fn sq(x: u32) -> Result { Ok(x * x) } fn err(x: u32) -> Result { Err(x) } assert_eq!(Ok(2).or_else(sq).or_else(sq), Ok(2)); assert_eq!(Ok(2).or_else(err).or_else(sq), Ok(2)); assert_eq!(Err(3).or_else(sq).or_else(err), Ok(9)); assert_eq!(Err(3).or_else(err).or_else(err), Err(3)); ``` -------------------------------- ### Test SDP display media with connection Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/sdp/builder.rs.html Verifies that a media-level connection is rendered as a 'c=' line in the SDP output. ```rust let addr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)); let mut sdp = SdpBuilder::new(addr) .add_media(MediaBuilder::audio(5000).pcmu()) .build(); // Add media-level connection sdp.media[0].connection = Some(Connection { net_type: "IN".to_string(), addr_type: "IP4".to_string(), address: "10.0.0.1".to_string(), }); let s = sdp.to_string(); assert!(s.contains("c=IN IP4 10.0.0.1")); ``` -------------------------------- ### TcpTransport::sender Source: https://docs.rs/rsiprtp/latest/rsiprtp/transport/struct.TcpTransport.html Gets a sender handle. ```APIDOC ## TcpTransport::sender ### Description Gets a sender handle. ### Method async ### Signature `pub fn sender(&self) -> TcpSender` ### Returns - `TcpSender` - A sender handle. ``` -------------------------------- ### Build WAV file header Source: https://docs.rs/rsiprtp/latest/src/rsiprtp/media/wav.rs.html Builds a 44-byte WAV header with RIFF, fmt, and data chunks. Assumes PCM format (audio format 1) and calculates byte rate, block align, and file size from the provided parameters. ```rust fn build_wav_header( sample_rate: u32, channels: u16, bits_per_sample: u16, data_size: u32, ) -> Vec { let byte_rate = sample_rate * channels as u32 * (bits_per_sample / 8) as u32; let block_align = channels * (bits_per_sample / 8); let file_size = 36 + data_size; let mut header = Vec::with_capacity(44); // RIFF header header.extend_from_slice(b"RIFF"); header.extend_from_slice(&file_size.to_le_bytes()); header.extend_from_slice(b"WAVE"); // fmt chunk header.extend_from_slice(b"fmt "); header.extend_from_slice(&16u32.to_le_bytes()); // Chunk size header.extend_from_slice(&1u16.to_le_bytes()); // Audio format (PCM) header.extend_from_slice(&channels.to_le_bytes()); header.extend_from_slice(&sample_rate.to_le_bytes()); header.extend_from_slice(&byte_rate.to_le_bytes()); header.extend_from_slice(&block_align.to_le_bytes()); header.extend_from_slice(&bits_per_sample.to_le_bytes()); // data chunk header.extend_from_slice(b"data"); header.extend_from_slice(&data_size.to_le_bytes()); header } ``` -------------------------------- ### NonInviteServerTransaction::state Source: https://docs.rs/rsiprtp/latest/rsiprtp/transaction/server/non_invite/struct.NonInviteServerTransaction.html Get the current state. ```APIDOC ## NonInviteServerTransaction::state ### Description Get the current state. ### Method Getter ### Signature `pub fn state(&self) -> State` ### Returns - **State** - The current state of the transaction. ``` -------------------------------- ### NonInviteServerTransaction::id Source: https://docs.rs/rsiprtp/latest/rsiprtp/transaction/server/non_invite/struct.NonInviteServerTransaction.html Get the transaction ID. ```APIDOC ## NonInviteServerTransaction::id ### Description Get the transaction ID. ### Method Getter ### Signature `pub fn id(&self) -> &TransactionId` ### Returns - **&TransactionId** - A reference to the transaction ID. ``` -------------------------------- ### active_calls Source: https://docs.rs/rsiprtp/latest/rsiprtp/session/struct.CallManager.html Get all active call IDs. ```APIDOC ## active_calls ### Description Get all active call IDs. ### Method pub fn ### Signature pub fn active_calls(&self) -> Vec ### Parameters None. ### Returns - **Vec** - The list of active call IDs. ``` -------------------------------- ### NonInviteClientTransaction::id Source: https://docs.rs/rsiprtp/latest/rsiprtp/transaction/client/non_invite/struct.NonInviteClientTransaction.html Gets the transaction ID. ```APIDOC ## NonInviteClientTransaction::id ### Description Gets the transaction ID. ### Method id ### Parameters None ### Returns - **&TransactionId** - The transaction ID. ``` -------------------------------- ### OpusCodec::bitrate Source: https://docs.rs/rsiprtp/latest/rsiprtp/media/struct.OpusCodec.html Gets the configured bitrate. ```APIDOC ## OpusCodec::bitrate ### Description Gets the configured bitrate. ### Method bitrate ### Signature `pub fn bitrate(&self) -> Bitrate` ### Parameters None ### Returns - `Bitrate` - The configured bitrate. ```