pub struct DialogClient { /* private fields */ }
Expand description
High-level client interface for SIP dialog management
The DialogClient provides a comprehensive, easy-to-use interface for client-side SIP operations. It handles the complexities of SIP dialog management while offering powerful features for call control, media negotiation, and session coordination.
§Key Capabilities
- Call Management: Create, control, and terminate voice/video calls
- Dialog Operations: Full SIP dialog lifecycle management
- Media Coordination: Integration with media layers through session events
- Request/Response Handling: Send arbitrary SIP methods and build responses
- Authentication: Built-in support for SIP authentication challenges
- Statistics & Monitoring: Real-time metrics and dialog state tracking
§Constructor Patterns
The DialogClient supports multiple construction patterns to fit different use cases:
§Pattern 1: Dependency Injection (Recommended)
use rvoip_dialog_core::api::{DialogClient, ClientConfig};
use rvoip_transaction_core::TransactionManager;
use std::sync::Arc;
// Your application manages transport
let tx_mgr = Arc::new(TransactionManager::new_sync(transport));
let config = ClientConfig::new("127.0.0.1:0".parse()?);
let client = DialogClient::with_dependencies(tx_mgr, config).await?;
§Pattern 2: Global Events (Best for Event Processing)
use rvoip_dialog_core::api::{DialogClient, ClientConfig};
use rvoip_transaction_core::{TransactionManager, TransactionEvent};
use tokio::sync::mpsc;
use std::sync::Arc;
let tx_mgr = Arc::new(TransactionManager::new_sync(transport));
let (events_tx, events_rx) = mpsc::channel(1000);
let config = ClientConfig::new("127.0.0.1:0".parse()?);
let client = DialogClient::with_global_events(tx_mgr, events_rx, config).await?;
§Complete Usage Example
use rvoip_dialog_core::api::{DialogClient, DialogApi, ClientConfig};
use rvoip_transaction_core::TransactionManager;
use std::sync::Arc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Set up dependencies
let tx_mgr = Arc::new(TransactionManager::new_sync(transport));
let config = ClientConfig::new("192.168.1.100:5060".parse()?)
.with_from_uri("sip:alice@company.com")
.with_auth("alice", "secret123");
// Create and start client
let client = DialogClient::with_dependencies(tx_mgr, config).await?;
client.start().await?;
// Make an outgoing call
let call = client.make_call(
"sip:alice@company.com",
"sip:bob@partner.com",
Some("v=0\r\no=alice 123 456 IN IP4 192.168.1.100\r\n...".to_string())
).await?;
println!("Call initiated: {}", call.call_id());
// Call operations
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
call.hold(Some("SDP with hold attributes".to_string())).await?;
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
call.resume(Some("SDP with active media".to_string())).await?;
// Transfer the call
call.transfer("sip:voicemail@company.com".to_string()).await?;
// Get statistics
let stats = client.get_stats().await;
println!("Client stats: {} active dialogs, {} total",
stats.active_dialogs, stats.total_dialogs);
// Clean shutdown
client.stop().await?;
Ok(())
}
§Advanced Dialog Operations
use rvoip_dialog_core::api::DialogClient;
use rvoip_sip_core::{Method, StatusCode};
// Create a dialog without sending initial request
let dialog = client.create_dialog("sip:me@here.com", "sip:you@there.com").await?;
// Send custom SIP methods within the dialog
let info_tx = client.send_info(&dialog.id(), "Application-specific data".to_string()).await?;
let notify_tx = client.send_notify(&dialog.id(), "presence".to_string(), Some("online".to_string())).await?;
let update_tx = client.send_update(&dialog.id(), Some("Updated media SDP".to_string())).await?;
// Monitor dialog state
let state = client.get_dialog_state(&dialog.id()).await?;
println!("Dialog state: {:?}", state);
// Build and send a response (if acting as UAS)
let response = client.build_response(&transaction_id, StatusCode::Ok, Some("Success".to_string())).await?;
client.send_response(&transaction_id, response).await?;
// Terminate the dialog
client.terminate_dialog(&dialog.id()).await?;
§Session Coordination Integration
use rvoip_dialog_core::api::{DialogClient, DialogApi};
use rvoip_dialog_core::events::SessionCoordinationEvent;
use tokio::sync::mpsc;
// Set up session coordination for media management
let (session_tx, mut session_rx) = mpsc::channel(100);
client.set_session_coordinator(session_tx).await?;
client.start().await?;
// Handle session events from the client
let session_handler = tokio::spawn(async move {
while let Some(event) = session_rx.recv().await {
match event {
SessionCoordinationEvent::IncomingCall { dialog_id, request, .. } => {
println!("Incoming call on dialog {}", dialog_id);
// Extract SDP, set up media, send response
},
SessionCoordinationEvent::CallAnswered { dialog_id, session_answer } => {
println!("Call {} answered", dialog_id);
// Configure media streams based on SDP answer
},
SessionCoordinationEvent::CallTerminated { dialog_id, reason } => {
println!("Call {} terminated: {}", dialog_id, reason);
// Clean up media resources
},
_ => {}
}
}
});
// Make calls while session handler processes events
let call = client.make_call("sip:me@here.com", "sip:you@there.com", None).await?;
// Session events will be automatically generated and handled
tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
call.hangup().await?;
§Error Handling Strategies
use rvoip_dialog_core::api::{DialogClient, ApiError};
use tokio::time::{sleep, Duration};
// Retry logic for network errors
let mut attempts = 0;
let max_attempts = 3;
loop {
match client.make_call("sip:me@here.com", "sip:you@there.com", None).await {
Ok(call) => {
println!("Call successful: {}", call.call_id());
break;
},
Err(ApiError::Network { message }) if attempts < max_attempts => {
attempts += 1;
println!("Network error (attempt {}): {}", attempts, message);
sleep(Duration::from_secs(2u64.pow(attempts))).await; // Exponential backoff
continue;
},
Err(ApiError::Configuration { message }) => {
eprintln!("Configuration error: {}", message);
// Fix configuration and restart
break;
},
Err(ApiError::Protocol { message }) => {
eprintln!("SIP protocol error: {}", message);
// Log for debugging, continue with other operations
break;
},
Err(e) => {
eprintln!("Unrecoverable error: {}", e);
break;
}
}
}
Implementations§
Source§impl DialogClient
impl DialogClient
Sourcepub async fn new(local_address: &str) -> ApiResult<Self>
pub async fn new(local_address: &str) -> ApiResult<Self>
Create a new dialog client with simple configuration
This is the easiest way to create a client - just provide a local address and the client will be configured with sensible defaults.
§Arguments
local_address
- Local address to use (e.g., “127.0.0.1:0”)
§Returns
A configured DialogClient ready to start
Sourcepub async fn with_config(config: ClientConfig) -> ApiResult<Self>
pub async fn with_config(config: ClientConfig) -> ApiResult<Self>
Create a dialog client with custom configuration
ARCHITECTURAL NOTE: This method requires dependency injection to maintain proper separation of concerns. dialog-core should not directly manage transport concerns - that’s the responsibility of transaction-core.
Use with_global_events()
or with_dependencies()
instead, where you provide
a pre-configured TransactionManager that handles all transport setup.
§Arguments
config
- Client configuration (for validation and future use)
§Returns
An error directing users to the proper dependency injection constructors
Sourcepub async fn with_global_events(
transaction_manager: Arc<TransactionManager>,
transaction_events: Receiver<TransactionEvent>,
config: ClientConfig,
) -> ApiResult<Self>
pub async fn with_global_events( transaction_manager: Arc<TransactionManager>, transaction_events: Receiver<TransactionEvent>, config: ClientConfig, ) -> ApiResult<Self>
Create a dialog client with dependency injection and global events (RECOMMENDED)
This is the recommended constructor for production applications. It uses global transaction event subscription which provides better event handling reliability and prevents event loss that can occur with per-transaction subscriptions.
§Why Global Events?
- Reliable Event Processing: All transaction events flow through a single channel
- No Event Loss: Events are queued and processed in order
- Better Resource Management: Single event loop handles all transactions
- Cleaner Architecture: Separation between event generation and consumption
§Usage Pattern
use rvoip_dialog_core::api::{DialogClient, DialogApi, ClientConfig};
use rvoip_transaction_core::{TransactionManager, TransactionEvent};
use tokio::sync::mpsc;
use std::sync::Arc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Set up transport layer (your responsibility)
// Create transaction manager
let tx_mgr = Arc::new(TransactionManager::new_sync(transport));
// Set up global event channel
let (event_tx, event_rx) = mpsc::channel(1000);
// Configure client
let config = ClientConfig::new("192.168.1.100:5060".parse()?)
.with_from_uri("sip:client@company.com")
.with_auth("client_user", "password123");
// Create client with global events
let client = DialogClient::with_global_events(tx_mgr, event_rx, config).await?;
// Start client and begin processing
client.start().await?;
// Client is now ready for operations
let call = client.make_call(
"sip:client@company.com",
"sip:target@partner.com",
None
).await?;
// Clean shutdown
call.hangup().await?;
client.stop().await?;
Ok(())
}
§Advanced Event Processing
use rvoip_dialog_core::api::{DialogClient, ClientConfig, DialogApi};
use rvoip_transaction_core::{TransactionManager, TransactionEvent};
use tokio::sync::mpsc;
use std::sync::Arc;
let tx_mgr = Arc::new(TransactionManager::new_sync(transport));
let (event_tx, event_rx) = mpsc::channel(1000);
let config = ClientConfig::new("127.0.0.1:0".parse()?);
// Create client
let client = DialogClient::with_global_events(tx_mgr.clone(), event_rx, config).await?;
// You can also handle transaction events directly if needed
tokio::spawn(async move {
// This would be your custom transaction event processor
// The client gets its own copy of events through the channel
});
client.start().await?;
§Arguments
transaction_manager
- Pre-configured transaction manager with transporttransaction_events
- Global transaction event receiver channelconfig
- Client configuration with network and authentication settings
§Returns
A configured DialogClient ready to start processing
§Errors
ApiError::Configuration
- Invalid configuration parametersApiError::Internal
- Failed to create dialog manager with global events
Sourcepub async fn with_dependencies(
transaction_manager: Arc<TransactionManager>,
config: ClientConfig,
) -> ApiResult<Self>
pub async fn with_dependencies( transaction_manager: Arc<TransactionManager>, config: ClientConfig, ) -> ApiResult<Self>
Create a dialog client with dependency injection
This constructor provides direct dependency injection for scenarios where you need full control over the dependencies. It’s particularly useful for testing, legacy integration, or when you have existing infrastructure to integrate with.
§When to Use This Pattern
- Testing: When you need to inject mock dependencies
- Legacy Integration: When working with existing transaction managers
- Simple Use Cases: When you don’t need complex event processing
- Custom Event Handling: When you want to handle transaction events differently
§Production Considerations
⚠️ Warning: This method uses individual transaction event subscriptions which
may not be as reliable as global event subscriptions. For production applications,
consider using with_global_events()
instead.
§Usage Examples
§Basic Usage
use rvoip_dialog_core::api::{DialogClient, DialogApi, ClientConfig};
use rvoip_transaction_core::TransactionManager;
use std::sync::Arc;
// Set up transport (your responsibility)
let tx_mgr = Arc::new(TransactionManager::new_sync(transport));
let config = ClientConfig::new("127.0.0.1:0".parse()?);
// Create client with dependency injection
let client = DialogClient::with_dependencies(tx_mgr, config).await?;
client.start().await?;
// Client is ready for use
let stats = client.get_stats().await;
println!("Client initialized: {} active dialogs", stats.active_dialogs);
§Testing Pattern
use rvoip_dialog_core::api::{DialogClient, ClientConfig};
use rvoip_transaction_core::TransactionManager;
use std::sync::Arc;
// Create mock transport for testing
let tx_mgr = Arc::new(TransactionManager::new_sync(mock_transport));
let config = ClientConfig::new("127.0.0.1:0".parse()?)
.with_from_uri("sip:test@localhost");
let client = DialogClient::with_dependencies(tx_mgr, config).await?;
// Test client operations
assert_eq!(client.config().from_uri.as_ref().unwrap(), "sip:test@localhost");
§Integration with Existing Infrastructure
use rvoip_dialog_core::api::{DialogClient, ClientConfig, DialogApi};
use rvoip_transaction_core::TransactionManager;
use std::sync::Arc;
// Use existing transaction manager from your infrastructure
let config = ClientConfig::new("10.0.1.50:5060".parse()?)
.with_from_uri("sip:service@internal.company.com")
.with_auth("service", "internal_password");
let client = DialogClient::with_dependencies(existing_tx_mgr, config).await?;
// Integrate with existing systems
client.start().await?;
§Arguments
transaction_manager
- Pre-configured transaction manager with transportconfig
- Client configuration with network and authentication settings
§Returns
A configured DialogClient ready to start processing
§Errors
ApiError::Configuration
- Invalid configuration parametersApiError::Internal
- Failed to create dialog manager
Sourcepub async fn make_call(
&self,
from_uri: &str,
to_uri: &str,
sdp_offer: Option<String>,
) -> ApiResult<CallHandle>
pub async fn make_call( &self, from_uri: &str, to_uri: &str, sdp_offer: Option<String>, ) -> ApiResult<CallHandle>
Make an outgoing call
Creates a new SIP dialog and initiates an outgoing call by sending an INVITE request. This is the primary method for establishing voice/video calls and handles all the complexity of SIP dialog creation, request generation, and initial negotiation.
§Call Flow
- URI Validation: Validates and parses the provided SIP URIs
- Dialog Creation: Creates a new outgoing dialog with unique identifiers
- INVITE Generation: Builds and sends an INVITE request with optional SDP
- Handle Creation: Returns a CallHandle for subsequent call operations
- Statistics Update: Updates internal call tracking metrics
§Usage Examples
§Basic Call
use rvoip_dialog_core::api::DialogClient;
let call = client.make_call(
"sip:alice@company.com", // Who the call is from
"sip:bob@partner.com", // Who to call
None // No SDP offer (late negotiation)
).await?;
println!("Call initiated: {}", call.call_id());
// Call is now ringing, wait for response...
§Call with SDP Offer (Early Media Negotiation)
use rvoip_dialog_core::api::DialogClient;
let sdp_offer = r#"v=0
o=alice 2890844526 2890844527 IN IP4 192.168.1.100
s=Call
c=IN IP4 192.168.1.100
t=0 0
m=audio 5004 RTP/AVP 0
a=rtpmap:0 PCMU/8000"#;
let call = client.make_call(
"sip:alice@company.com",
"sip:bob@partner.com",
Some(sdp_offer.to_string())
).await?;
println!("Call with media offer sent: {}", call.call_id());
§Production Call with Error Handling
use rvoip_dialog_core::api::{DialogClient, ApiError};
use tokio::time::{timeout, Duration};
// Set a timeout for the call setup
let call_result = timeout(Duration::from_secs(30), async {
client.make_call(
"sip:service@company.com",
"sip:customer@external.com",
None
).await
}).await;
match call_result {
Ok(Ok(call)) => {
println!("Call successful: {}", call.call_id());
// Set up call monitoring
tokio::spawn(async move {
if call.is_active().await {
println!("Call is active and can be managed");
}
});
},
Ok(Err(ApiError::Configuration { message })) => {
eprintln!("Configuration error: {}", message);
// Fix URIs and retry
},
Ok(Err(ApiError::Network { message })) => {
eprintln!("Network error: {}", message);
// Check connectivity and retry
},
Err(_) => {
eprintln!("Call setup timed out");
// Handle timeout scenario
},
Ok(Err(e)) => {
eprintln!("Call failed: {}", e);
}
}
§Batch Call Operations
use rvoip_dialog_core::api::DialogClient;
let call_targets = vec![
("sip:alice@company.com", "sip:customer1@external.com"),
("sip:alice@company.com", "sip:customer2@external.com"),
("sip:alice@company.com", "sip:customer3@external.com"),
];
// Make multiple calls sequentially for simplicity
let mut calls = Vec::new();
for (from, to) in call_targets {
let call = client.make_call(from, to, None).await?;
calls.push(call);
}
println!("Successfully initiated {} calls", calls.len());
// Monitor all calls
for call in calls {
let info = call.info().await?;
println!("Call {}: {} -> {} ({})",
call.call_id(), info.local_uri, info.remote_uri, info.state);
}
§Arguments
from_uri
- Local SIP URI (From header) - who the call is fromto_uri
- Remote SIP URI (To header) - who to callsdp_offer
- Optional SDP offer for media negotiation
§Returns
A CallHandle for managing the call lifecycle and operations
§Errors
ApiError::Configuration
- Invalid SIP URIs providedApiError::Dialog
- Failed to create dialog or send INVITEApiError::Internal
- Internal dialog manager error
Sourcepub async fn create_dialog(
&self,
from_uri: &str,
to_uri: &str,
) -> ApiResult<DialogHandle>
pub async fn create_dialog( &self, from_uri: &str, to_uri: &str, ) -> ApiResult<DialogHandle>
Create a new dialog without sending a request
Creates a SIP dialog without immediately sending an INVITE or other initial request. This is useful for advanced scenarios where you need fine-grained control over the dialog lifecycle, want to send custom requests, or need to prepare dialogs for specific protocol sequences.
§When to Use This Method
- Custom Protocol Sequences: When you need to send non-INVITE initial requests
- Conditional Call Setup: When call initiation depends on external factors
- Advanced Dialog Management: When you need dialog state before sending requests
- Testing Scenarios: When you want to test dialog creation independently
- Batch Operations: When preparing multiple dialogs for coordinated operations
§Usage Examples
§Basic Dialog Creation
use rvoip_dialog_core::api::DialogClient;
use rvoip_sip_core::Method;
// Create dialog without initial request
let dialog = client.create_dialog(
"sip:alice@company.com",
"sip:bob@partner.com"
).await?;
println!("Dialog created: {}", dialog.id());
// Now you can send custom requests
dialog.send_request(Method::Options, None).await?;
dialog.send_request(Method::Info, Some("Custom info".to_string())).await?;
// Check dialog state
let state = dialog.state().await?;
println!("Dialog state: {:?}", state);
§Conditional Call Setup
use rvoip_dialog_core::api::DialogClient;
use rvoip_sip_core::Method;
// Create dialog first
let dialog = client.create_dialog(
"sip:service@company.com",
"sip:customer@external.com"
).await?;
// Check external conditions before proceeding
if check_customer_availability().await? {
// Send INVITE if customer is available
dialog.send_request(Method::Invite, Some("SDP offer".to_string())).await?;
println!("Call initiated for available customer");
} else {
// Send MESSAGE instead
dialog.send_request(Method::Message, Some("Customer callback requested".to_string())).await?;
println!("Message sent to unavailable customer");
}
§Advanced Protocol Sequences
use rvoip_dialog_core::api::DialogClient;
use rvoip_sip_core::Method;
let dialog = client.create_dialog(
"sip:alice@company.com",
"sip:conference@partner.com"
).await?;
// Custom protocol: Send OPTIONS first to check capabilities
let options_tx = dialog.send_request(Method::Options, None).await?;
println!("Sent OPTIONS: {}", options_tx);
// Wait for response processing (in real code, you'd handle this via events)
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
// Send SUBSCRIBE for presence information
let subscribe_tx = dialog.send_request(
Method::Subscribe,
Some("Event: presence\r\nExpires: 3600".to_string())
).await?;
println!("Sent SUBSCRIBE: {}", subscribe_tx);
// Finally, send INVITE for the actual call
let invite_tx = dialog.send_request(Method::Invite, Some("SDP offer".to_string())).await?;
println!("Sent INVITE: {}", invite_tx);
§Batch Dialog Preparation
use rvoip_dialog_core::api::DialogClient;
use std::future::Future;
let targets = vec![
"sip:customer1@external.com",
"sip:customer2@external.com",
"sip:customer3@external.com",
];
// Create multiple dialogs concurrently using join_all from std
let mut dialogs = Vec::new();
for target in targets {
let dialog = client.create_dialog("sip:service@company.com", target).await?;
dialogs.push(dialog);
}
println!("Created {} dialogs", dialogs.len());
// Now you can coordinate operations across all dialogs
for (i, dialog) in dialogs.iter().enumerate() {
// Stagger call initiation
tokio::time::sleep(tokio::time::Duration::from_millis(i as u64 * 100)).await;
dialog.send_request(rvoip_sip_core::Method::Invite, None).await?;
}
§Arguments
from_uri
- Local SIP URI (From header)to_uri
- Remote SIP URI (To header)
§Returns
A DialogHandle for the new dialog
§Errors
ApiError::Configuration
- Invalid SIP URIs providedApiError::Dialog
- Failed to create dialogApiError::Internal
- Internal dialog manager error
Sourcepub async fn send_request_in_dialog(
&self,
dialog_id: &DialogId,
method: Method,
body: Option<Bytes>,
) -> ApiResult<TransactionKey>
pub async fn send_request_in_dialog( &self, dialog_id: &DialogId, method: Method, body: Option<Bytes>, ) -> ApiResult<TransactionKey>
Send a SIP request within an existing dialog
Sends arbitrary SIP methods within an established dialog. This method provides direct access to SIP protocol operations and is essential for session-core coordination, custom protocol implementations, and advanced call control.
§Supported SIP Methods
This method can send any SIP method within a dialog context:
- INVITE: Re-INVITE for media changes, call transfers
- BYE: Call termination (prefer using
send_bye()
convenience method) - UPDATE: Media parameter updates (RFC 3311)
- INFO: Application-specific information (RFC 6086)
- REFER: Call transfers and redirections (RFC 3515)
- NOTIFY: Event notifications (RFC 3265)
- MESSAGE: Instant messaging within dialogs
- OPTIONS: Capability queries
- SUBSCRIBE: Event subscriptions
§Usage Examples
§Media Management with UPDATE
use rvoip_dialog_core::api::DialogClient;
use rvoip_sip_core::Method;
use rvoip_dialog_core::dialog::DialogId;
// Send UPDATE to modify media parameters
let updated_sdp = r#"v=0
o=alice 2890844526 2890844528 IN IP4 192.168.1.100
s=Updated Call
c=IN IP4 192.168.1.100
t=0 0
m=audio 5004 RTP/AVP 0 8
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000"#;
let tx_key = client.send_request_in_dialog(
&dialog_id,
Method::Update,
Some(bytes::Bytes::from(updated_sdp))
).await?;
println!("Sent UPDATE request: {}", tx_key);
§Application Information Exchange
use rvoip_dialog_core::api::DialogClient;
use rvoip_sip_core::Method;
use rvoip_dialog_core::dialog::DialogId;
// Send application-specific information
let app_data = r#"Content-Type: application/json
{
"action": "screen_share_request",
"timestamp": "2024-01-15T10:30:00Z",
"session_id": "abc123"
}"#;
let tx_key = client.send_request_in_dialog(
&dialog_id,
Method::Info,
Some(bytes::Bytes::from(app_data))
).await?;
println!("Sent INFO with application data: {}", tx_key);
§Event Notifications
use rvoip_dialog_core::api::DialogClient;
use rvoip_sip_core::Method;
use rvoip_dialog_core::dialog::DialogId;
// Send NOTIFY for presence update
let notify_body = r#"Event: presence
Subscription-State: active;expires=3600
Content-Type: application/pidf+xml
<?xml version="1.0" encoding="UTF-8"?>
<presence xmlns="urn:ietf:params:xml:ns:pidf" entity="sip:alice@company.com">
<tuple id="tuple1">
<status><basic>open</basic></status>
<contact>sip:alice@company.com</contact>
</tuple>
</presence>"#;
let tx_key = client.send_request_in_dialog(
&dialog_id,
Method::Notify,
Some(bytes::Bytes::from(notify_body))
).await?;
println!("Sent NOTIFY for presence: {}", tx_key);
§Call Transfer with REFER
use rvoip_dialog_core::api::DialogClient;
use rvoip_sip_core::Method;
use rvoip_dialog_core::dialog::DialogId;
// Send REFER for call transfer
let refer_body = r#"Refer-To: sip:bob@partner.com
Referred-By: sip:alice@company.com
Contact: sip:alice@company.com
Content-Length: 0"#;
let tx_key = client.send_request_in_dialog(
&dialog_id,
Method::Refer,
Some(bytes::Bytes::from(refer_body))
).await?;
println!("Sent REFER for transfer: {}", tx_key);
§Session Coordination Pattern
use rvoip_dialog_core::api::DialogClient;
use rvoip_sip_core::Method;
use rvoip_dialog_core::dialog::DialogId;
// Coordinate with session layer for media changes
let media_session = setup_media_session().await?;
let new_sdp = media_session.generate_offer().await?;
// Send re-INVITE with new media parameters
let tx_key = client.send_request_in_dialog(
&dialog_id,
Method::Invite,
Some(bytes::Bytes::from(new_sdp))
).await?;
println!("Sent re-INVITE for media change: {}", tx_key);
// The response will be handled by session coordination events
§Arguments
dialog_id
- The dialog ID to send the request withinmethod
- SIP method to send (INVITE, BYE, UPDATE, INFO, etc.)body
- Optional message body (SDP, application data, etc.)
§Returns
Transaction key for tracking the request and its response
§Errors
ApiError::Dialog
- Dialog not found or invalid stateApiError::Protocol
- Invalid method for current dialog stateApiError::Internal
- Failed to send request
Sourcepub async fn get_dialog_info(&self, dialog_id: &DialogId) -> ApiResult<Dialog>
pub async fn get_dialog_info(&self, dialog_id: &DialogId) -> ApiResult<Dialog>
Sourcepub async fn get_dialog_state(
&self,
dialog_id: &DialogId,
) -> ApiResult<DialogState>
pub async fn get_dialog_state( &self, dialog_id: &DialogId, ) -> ApiResult<DialogState>
Sourcepub async fn terminate_dialog(&self, dialog_id: &DialogId) -> ApiResult<()>
pub async fn terminate_dialog(&self, dialog_id: &DialogId) -> ApiResult<()>
Sourcepub async fn list_active_dialogs(&self) -> Vec<DialogId>
pub async fn list_active_dialogs(&self) -> Vec<DialogId>
List all active dialog IDs
Provides access to all active dialogs for monitoring and management purposes.
§Returns
Vector of active dialog IDs
Sourcepub async fn send_response(
&self,
transaction_id: &TransactionKey,
response: Response,
) -> ApiResult<()>
pub async fn send_response( &self, transaction_id: &TransactionKey, response: Response, ) -> ApiResult<()>
Sourcepub async fn build_response(
&self,
transaction_id: &TransactionKey,
status_code: StatusCode,
body: Option<String>,
) -> ApiResult<Response>
pub async fn build_response( &self, transaction_id: &TransactionKey, status_code: StatusCode, body: Option<String>, ) -> ApiResult<Response>
Build a SIP response with automatic header generation
Convenience method for creating properly formatted SIP responses with correct headers and routing information using Phase 3 dialog functions.
§Arguments
transaction_id
- Transaction to respond tostatus_code
- SIP status codebody
- Optional response body
§Returns
Built SIP response ready for sending
Sourcepub async fn build_dialog_response(
&self,
transaction_id: &TransactionKey,
dialog_id: &DialogId,
status_code: StatusCode,
body: Option<String>,
) -> ApiResult<Response>
pub async fn build_dialog_response( &self, transaction_id: &TransactionKey, dialog_id: &DialogId, status_code: StatusCode, body: Option<String>, ) -> ApiResult<Response>
Build a dialog-aware response with enhanced context
This method provides dialog-aware response building using Phase 3 dialog utilities to ensure proper response construction for dialog transactions.
§Arguments
transaction_id
- Transaction to respond todialog_id
- Dialog ID for contextstatus_code
- SIP status codebody
- Optional response body
§Returns
Built SIP response with dialog awareness
Sourcepub async fn send_status_response(
&self,
transaction_id: &TransactionKey,
status_code: StatusCode,
reason: Option<String>,
) -> ApiResult<()>
pub async fn send_status_response( &self, transaction_id: &TransactionKey, status_code: StatusCode, reason: Option<String>, ) -> ApiResult<()>
Sourcepub async fn send_bye(&self, dialog_id: &DialogId) -> ApiResult<TransactionKey>
pub async fn send_bye(&self, dialog_id: &DialogId) -> ApiResult<TransactionKey>
Sourcepub async fn send_refer(
&self,
dialog_id: &DialogId,
target_uri: String,
refer_body: Option<String>,
) -> ApiResult<TransactionKey>
pub async fn send_refer( &self, dialog_id: &DialogId, target_uri: String, refer_body: Option<String>, ) -> ApiResult<TransactionKey>
Send a REFER request for call transfer
Convenience method for initiating call transfers using the REFER method as defined in RFC 3515.
§Arguments
dialog_id
- Dialog to send REFER withintarget_uri
- URI to transfer the call torefer_body
- Optional REFER body with additional headers
§Returns
Transaction key for the REFER request
Sourcepub async fn send_notify(
&self,
dialog_id: &DialogId,
event: String,
body: Option<String>,
) -> ApiResult<TransactionKey>
pub async fn send_notify( &self, dialog_id: &DialogId, event: String, body: Option<String>, ) -> ApiResult<TransactionKey>
Send a NOTIFY request for event notifications
Convenience method for sending event notifications using the NOTIFY method as defined in RFC 3265.
§Arguments
dialog_id
- Dialog to send NOTIFY withinevent
- Event type being notifiedbody
- Optional notification body
§Returns
Transaction key for the NOTIFY request
Sourcepub async fn send_update(
&self,
dialog_id: &DialogId,
sdp: Option<String>,
) -> ApiResult<TransactionKey>
pub async fn send_update( &self, dialog_id: &DialogId, sdp: Option<String>, ) -> ApiResult<TransactionKey>
Sourcepub async fn send_info(
&self,
dialog_id: &DialogId,
info_body: String,
) -> ApiResult<TransactionKey>
pub async fn send_info( &self, dialog_id: &DialogId, info_body: String, ) -> ApiResult<TransactionKey>
Sourcepub fn config(&self) -> &ClientConfig
pub fn config(&self) -> &ClientConfig
Get client configuration
Sourcepub async fn active_dialogs(&self) -> Vec<DialogHandle>
pub async fn active_dialogs(&self) -> Vec<DialogHandle>
Get a list of all active dialog handles