Skip to content

Commit 781f7ea

Browse files
committed
feat(dialog): implement refer
1 parent 86b4d76 commit 781f7ea

File tree

9 files changed

+877
-59
lines changed

9 files changed

+877
-59
lines changed

examples/client/main.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,12 @@ async fn process_dialog(
388388
Dialog::ClientInvite(_) => {
389389
info!("Client invite dialog {}", id);
390390
}
391+
Dialog::ServerSubscription(_) => {
392+
info!("Server subscription dialog {}", id);
393+
}
394+
Dialog::ClientSubscription(_) => {
395+
info!("Client subscription dialog {}", id);
396+
}
391397
}
392398
}
393399
DialogState::Early(id, resp) => {
@@ -397,6 +403,12 @@ async fn process_dialog(
397403
info!("Dialog terminated {} {:?}", id, reason);
398404
dialog_layer.remove_dialog(&id);
399405
}
406+
DialogState::Refer(id, req, _handle) => {
407+
info!("Received REFER for dialog {}: {}", id, req.uri);
408+
}
409+
DialogState::Message(id, req, _handle) => {
410+
info!("Received MESSAGE for dialog {}: {}", id, req.uri);
411+
}
400412
_ => {
401413
info!("Received dialog state: {}", state);
402414
}

src/dialog/client_dialog.rs

Lines changed: 149 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use super::dialog::DialogInnerRef;
22
use super::DialogId;
33
use crate::dialog::{
44
authenticate::handle_client_authenticate,
5-
dialog::{DialogState, TerminatedReason},
5+
dialog::{DialogState, TerminatedReason, TransactionHandle},
6+
subscription::ClientSubscriptionDialog,
67
};
78
use crate::rsip_ext::RsipResponseExt;
89
use crate::transaction::transaction::Transaction;
@@ -12,7 +13,7 @@ use rsip::{prelude::HeadersExt, Header};
1213
use rsip::{Response, SipMessage, StatusCode};
1314
use std::sync::atomic::Ordering;
1415
use tokio_util::sync::CancellationToken;
15-
use tracing::{info, trace};
16+
use tracing::{debug, info, trace};
1617

1718
/// Client-side INVITE Dialog (UAC)
1819
///
@@ -257,8 +258,9 @@ impl ClientInviteDialog {
257258
match resp {
258259
Ok(Some(ref resp)) => {
259260
if resp.status_code == StatusCode::OK {
261+
let (handle, _) = TransactionHandle::new();
260262
self.inner
261-
.transition(DialogState::Updated(self.id(), request))?;
263+
.transition(DialogState::Updated(self.id(), request, handle))?;
262264
}
263265
}
264266
_ => {}
@@ -370,6 +372,104 @@ impl ClientInviteDialog {
370372
.make_request(rsip::Method::Options, None, None, None, headers, body)?;
371373
self.inner.do_request(request.clone()).await
372374
}
375+
376+
/// Send a generic in-dialog request
377+
///
378+
/// This method allows sending any SIP request within the dialog.
379+
/// It automatically handles CSeq increment, Call-ID, From/To tags, and Route set.
380+
pub async fn request(
381+
&self,
382+
method: rsip::Method,
383+
headers: Option<Vec<rsip::Header>>,
384+
body: Option<Vec<u8>>,
385+
) -> Result<Option<rsip::Response>> {
386+
if !self.inner.is_confirmed() {
387+
return Ok(None);
388+
}
389+
info!(id=%self.id(),"sending {} request", method);
390+
let request = self
391+
.inner
392+
.make_request(method, None, None, None, headers, body)?;
393+
self.inner.do_request(request).await
394+
}
395+
396+
/// Send a NOTIFY request
397+
pub async fn notify(
398+
&self,
399+
headers: Option<Vec<rsip::Header>>,
400+
body: Option<Vec<u8>>,
401+
) -> Result<Option<rsip::Response>> {
402+
self.request(rsip::Method::Notify, headers, body).await
403+
}
404+
405+
/// Send a REFER request
406+
///
407+
/// Sends a REFER request to transfer the call to another destination.
408+
///
409+
/// # Parameters
410+
///
411+
/// * `refer_to` - The URI to refer to (Refer-To header value)
412+
/// * `headers` - Optional additional headers
413+
/// * `body` - Optional message body
414+
pub async fn refer(
415+
&self,
416+
refer_to: rsip::Uri,
417+
headers: Option<Vec<rsip::Header>>,
418+
body: Option<Vec<u8>>,
419+
) -> Result<Option<rsip::Response>> {
420+
let mut headers = headers.unwrap_or_default();
421+
headers.push(rsip::Header::Other(
422+
"Refer-To".into(),
423+
format!("<{}>", refer_to).into(),
424+
));
425+
self.request(rsip::Method::Refer, Some(headers), body).await
426+
}
427+
428+
/// Send a REFER progress notification (RFC 3515)
429+
///
430+
/// This is used by the REFER recipient to notify the sender about the
431+
/// progress of the referred action.
432+
///
433+
/// # Parameters
434+
///
435+
/// * `status` - The status of the referred action (e.g., 100 Trying, 200 OK)
436+
/// * `sub_state` - The subscription state (e.g., "active", "terminated;reason=noresource")
437+
pub async fn notify_refer(
438+
&self,
439+
status: rsip::StatusCode,
440+
sub_state: &str,
441+
) -> Result<Option<rsip::Response>> {
442+
let headers = vec![
443+
rsip::Header::Other("Event".into(), "refer".into()),
444+
rsip::Header::Other("Subscription-State".into(), sub_state.into()),
445+
rsip::Header::ContentType("message/sipfrag".into()),
446+
];
447+
448+
let body = format!("SIP/2.0 {} {:?}", u16::from(status.clone()), status).into_bytes();
449+
450+
self.notify(Some(headers), Some(body)).await
451+
}
452+
453+
/// Convert this INVITE dialog to a subscription dialog
454+
///
455+
/// This is useful for handling implicit subscriptions created by REFER.
456+
pub fn as_subscription(&self) -> ClientSubscriptionDialog {
457+
ClientSubscriptionDialog {
458+
inner: self.inner.clone(),
459+
}
460+
}
461+
462+
/// Send a MESSAGE request
463+
///
464+
/// Sends an instant message within the dialog.
465+
pub async fn message(
466+
&self,
467+
headers: Option<Vec<rsip::Header>>,
468+
body: Option<Vec<u8>>,
469+
) -> Result<Option<rsip::Response>> {
470+
self.request(rsip::Method::Message, headers, body).await
471+
}
472+
373473
/// Handle incoming transaction for this dialog
374474
///
375475
/// Processes incoming SIP requests that are routed to this dialog.
@@ -420,6 +520,9 @@ impl ClientInviteDialog {
420520
rsip::Method::Info => return self.handle_info(tx).await,
421521
rsip::Method::Options => return self.handle_options(tx).await,
422522
rsip::Method::Update => return self.handle_update(tx).await,
523+
rsip::Method::Refer => return self.handle_refer(tx).await,
524+
rsip::Method::Message => return self.handle_message(tx).await,
525+
rsip::Method::Notify => return self.handle_notify(tx).await,
423526
_ => {
424527
info!(id=%self.id(), "invalid request method: {:?}", tx.original.method);
425528
tx.reply(rsip::StatusCode::MethodNotAllowed).await?;
@@ -448,34 +551,36 @@ impl ClientInviteDialog {
448551
}
449552

450553
async fn handle_info(&mut self, tx: &mut Transaction) -> Result<()> {
451-
info!(id=%self.id(),"received info {}", tx.original.uri);
554+
debug!(id=%self.id(),"received info {}", tx.original.uri);
555+
let (handle, rx) = TransactionHandle::new();
452556
self.inner
453-
.transition(DialogState::Info(self.id(), tx.original.clone()))?;
454-
tx.reply(rsip::StatusCode::OK).await?;
455-
Ok(())
557+
.transition(DialogState::Info(self.id(), tx.original.clone(), handle))?;
558+
self.inner.process_transaction_handle(tx, rx).await
456559
}
457560

458561
async fn handle_options(&mut self, tx: &mut Transaction) -> Result<()> {
459-
info!(id=%self.id(),"received options {}", tx.original.uri);
562+
debug!(id=%self.id(),"received options {}", tx.original.uri);
563+
let (handle, rx) = TransactionHandle::new();
460564
self.inner
461-
.transition(DialogState::Options(self.id(), tx.original.clone()))?;
462-
tx.reply(rsip::StatusCode::OK).await?;
463-
Ok(())
565+
.transition(DialogState::Options(self.id(), tx.original.clone(), handle))?;
566+
self.inner.process_transaction_handle(tx, rx).await
464567
}
465568

466569
async fn handle_update(&mut self, tx: &mut Transaction) -> Result<()> {
467-
info!(id=%self.id(),"received update {}", tx.original.uri);
570+
debug!(id=%self.id(),"received update {}", tx.original.uri);
571+
let (handle, rx) = TransactionHandle::new();
468572
self.inner
469-
.transition(DialogState::Updated(self.id(), tx.original.clone()))?;
470-
tx.reply(rsip::StatusCode::OK).await?;
471-
Ok(())
573+
.transition(DialogState::Updated(self.id(), tx.original.clone(), handle))?;
574+
self.inner.process_transaction_handle(tx, rx).await
472575
}
473576

474577
async fn handle_reinvite(&mut self, tx: &mut Transaction) -> Result<()> {
475-
info!(id=%self.id(),"received reinvite {}", tx.original.uri);
578+
debug!(id=%self.id(),"received reinvite {}", tx.original.uri);
579+
let (handle, rx) = TransactionHandle::new();
476580
self.inner
477-
.transition(DialogState::Updated(self.id(), tx.original.clone()))?;
478-
tx.reply(rsip::StatusCode::OK).await?;
581+
.transition(DialogState::Updated(self.id(), tx.original.clone(), handle))?;
582+
583+
self.inner.process_transaction_handle(tx, rx).await?;
479584

480585
// wait for ACK
481586
while let Some(msg) = tx.receive().await {
@@ -490,6 +595,32 @@ impl ClientInviteDialog {
490595
Ok(())
491596
}
492597

598+
async fn handle_refer(&mut self, tx: &mut Transaction) -> Result<()> {
599+
debug!(id=%self.id(),"received refer {}", tx.original.uri);
600+
let (handle, rx) = TransactionHandle::new();
601+
self.inner
602+
.transition(DialogState::Refer(self.id(), tx.original.clone(), handle))?;
603+
604+
self.inner.process_transaction_handle(tx, rx).await
605+
}
606+
607+
async fn handle_message(&mut self, tx: &mut Transaction) -> Result<()> {
608+
debug!(id=%self.id(),"received message {}", tx.original.uri);
609+
let (handle, rx) = TransactionHandle::new();
610+
self.inner
611+
.transition(DialogState::Message(self.id(), tx.original.clone(), handle))?;
612+
613+
self.inner.process_transaction_handle(tx, rx).await
614+
}
615+
616+
async fn handle_notify(&mut self, tx: &mut Transaction) -> Result<()> {
617+
debug!(id=%self.id(),"received notify {}", tx.original.uri);
618+
let (handle, rx) = TransactionHandle::new();
619+
self.inner
620+
.transition(DialogState::Notify(self.id(), tx.original.clone(), handle))?;
621+
self.inner.process_transaction_handle(tx, rx).await
622+
}
623+
493624
pub async fn process_invite(
494625
&self,
495626
mut tx: Transaction,

0 commit comments

Comments
 (0)