From 20870e667344087dc11720d8f13964805f16e063 Mon Sep 17 00:00:00 2001 From: yeoleobun Date: Mon, 20 Apr 2026 09:59:03 +0800 Subject: [PATCH] Support name-addr Refer-To headers --- src/dialog/client_dialog.rs | 9 ++---- src/dialog/dialog.rs | 2 +- src/dialog/publication.rs | 12 +++----- src/dialog/server_dialog.rs | 7 ++--- src/dialog/subscription.rs | 12 +++----- src/dialog/tests/mod.rs | 1 + src/dialog/tests/test_refer.rs | 54 ++++++++++++++++++++++++++++++++++ src/sip/headers/untyped.rs | 6 ++++ 8 files changed, 75 insertions(+), 28 deletions(-) create mode 100644 src/dialog/tests/test_refer.rs diff --git a/src/dialog/client_dialog.rs b/src/dialog/client_dialog.rs index 588a11ec..782cc418 100644 --- a/src/dialog/client_dialog.rs +++ b/src/dialog/client_dialog.rs @@ -471,20 +471,17 @@ impl ClientInviteDialog { /// /// # Parameters /// - /// * `refer_to` - The URI to refer to (Refer-To header value) + /// * `refer_to` - The full Refer-To header value. `Uri` inputs are serialized as ``. /// * `headers` - Optional additional headers /// * `body` - Optional message body pub async fn refer( &self, - refer_to: crate::sip::Uri, + refer_to: impl Into, headers: Option>, body: Option>, ) -> Result> { let mut headers = headers.unwrap_or_default(); - headers.push(crate::sip::Header::Other( - "Refer-To".into(), - format!("<{}>", refer_to), - )); + headers.push(crate::sip::Header::ReferTo(refer_to.into())); self.request(crate::sip::Method::Refer, Some(headers), body) .await } diff --git a/src/dialog/dialog.rs b/src/dialog/dialog.rs index ee591fb5..0ed9556a 100644 --- a/src/dialog/dialog.rs +++ b/src/dialog/dialog.rs @@ -1429,7 +1429,7 @@ impl Dialog { pub async fn refer( &self, - refer_to: crate::sip::Uri, + refer_to: impl Into, headers: Option>, body: Option>, ) -> Result> { diff --git a/src/dialog/publication.rs b/src/dialog/publication.rs index de3d93b2..f1800d26 100644 --- a/src/dialog/publication.rs +++ b/src/dialog/publication.rs @@ -96,14 +96,12 @@ impl ClientPublicationDialog { pub async fn refer( &self, - refer_to: crate::sip::Uri, + refer_to: impl Into, headers: Option>, body: Option>, ) -> Result> { let mut headers = headers.unwrap_or_default(); - headers.push(crate::sip::Header::ReferTo( - format!("<{}>", refer_to).into(), - )); + headers.push(crate::sip::Header::ReferTo(refer_to.into())); self.request(crate::sip::Method::Refer, Some(headers), body) .await } @@ -229,14 +227,12 @@ impl ServerPublicationDialog { pub async fn refer( &self, - refer_to: crate::sip::Uri, + refer_to: impl Into, headers: Option>, body: Option>, ) -> Result> { let mut headers = headers.unwrap_or_default(); - headers.push(crate::sip::Header::ReferTo( - format!("<{}>", refer_to).into(), - )); + headers.push(crate::sip::Header::ReferTo(refer_to.into())); self.request(crate::sip::Method::Refer, Some(headers), body) .await } diff --git a/src/dialog/server_dialog.rs b/src/dialog/server_dialog.rs index a7c7dffb..9761dd78 100644 --- a/src/dialog/server_dialog.rs +++ b/src/dialog/server_dialog.rs @@ -552,15 +552,12 @@ impl ServerInviteDialog { /// Send a REFER request pub async fn refer( &self, - refer_to: crate::sip::Uri, + refer_to: impl Into, headers: Option>, body: Option>, ) -> Result> { let mut headers = headers.unwrap_or_default(); - headers.push(crate::sip::Header::Other( - "Refer-To".into(), - format!("<{}>", refer_to), - )); + headers.push(crate::sip::Header::ReferTo(refer_to.into())); self.request(crate::sip::Method::Refer, Some(headers), body) .await } diff --git a/src/dialog/subscription.rs b/src/dialog/subscription.rs index 998a8bf9..d992b460 100644 --- a/src/dialog/subscription.rs +++ b/src/dialog/subscription.rs @@ -58,14 +58,12 @@ impl ClientSubscriptionDialog { pub async fn refer( &self, - refer_to: crate::sip::Uri, + refer_to: impl Into, headers: Option>, body: Option>, ) -> Result> { let mut headers = headers.unwrap_or_default(); - headers.push(crate::sip::Header::ReferTo( - format!("<{}>", refer_to).into(), - )); + headers.push(crate::sip::Header::ReferTo(refer_to.into())); self.request(crate::sip::Method::Refer, Some(headers), body) .await } @@ -193,14 +191,12 @@ impl ServerSubscriptionDialog { pub async fn refer( &self, - refer_to: crate::sip::Uri, + refer_to: impl Into, headers: Option>, body: Option>, ) -> Result> { let mut headers = headers.unwrap_or_default(); - headers.push(crate::sip::Header::ReferTo( - format!("<{}>", refer_to).into(), - )); + headers.push(crate::sip::Header::ReferTo(refer_to.into())); self.request(crate::sip::Method::Refer, Some(headers), body) .await } diff --git a/src/dialog/tests/mod.rs b/src/dialog/tests/mod.rs index 73e646f8..bdec1f84 100644 --- a/src/dialog/tests/mod.rs +++ b/src/dialog/tests/mod.rs @@ -3,5 +3,6 @@ mod test_client_dialog; mod test_dialog_layer; mod test_dialog_states; mod test_prack; +mod test_refer; mod test_server_dialog; mod test_sub_pub; diff --git a/src/dialog/tests/test_refer.rs b/src/dialog/tests/test_refer.rs new file mode 100644 index 00000000..84993e25 --- /dev/null +++ b/src/dialog/tests/test_refer.rs @@ -0,0 +1,54 @@ +use std::sync::Arc; + +use tokio::sync::mpsc::unbounded_channel; + +use crate::dialog::{client_dialog::ClientInviteDialog, dialog::DialogInner, DialogId}; +use crate::sip::{ReferTo, Uri}; +use crate::transaction::key::TransactionRole; + +use super::test_dialog_states::{create_invite_request, create_test_endpoint}; + +#[test] +fn test_refer_to_from_uri_preserves_existing_uri_behavior() -> crate::Result<()> { + let refer_to = ReferTo::from(Uri::try_from("sip:carol@restsend.com")?); + assert_eq!(refer_to.value(), ""); + Ok(()) +} + +#[tokio::test] +async fn test_client_dialog_refer_accepts_name_addr_values() -> crate::Result<()> { + let endpoint = create_test_endpoint().await?; + let (state_sender, _) = unbounded_channel(); + + let dialog_id = DialogId { + call_id: "refer-name-addr".to_string(), + local_tag: "alice-tag".to_string(), + remote_tag: "bob-tag".to_string(), + }; + + let invite_req = create_invite_request("alice-tag", "bob-tag", "refer-name-addr"); + let (tu_sender, _tu_receiver) = unbounded_channel(); + + let dialog_inner = DialogInner::new( + TransactionRole::Client, + dialog_id, + invite_req, + endpoint.inner.clone(), + state_sender, + None, + Some(Uri::try_from("sip:alice@alice.example.com:5060")?), + tu_sender, + )?; + + let client_dialog = ClientInviteDialog { + inner: Arc::new(dialog_inner), + }; + + std::mem::drop(client_dialog.refer( + "\"Display Name\" ", + None, + None, + )); + + Ok(()) +} diff --git a/src/sip/headers/untyped.rs b/src/sip/headers/untyped.rs index b1a7ab20..ae13cd12 100644 --- a/src/sip/headers/untyped.rs +++ b/src/sip/headers/untyped.rs @@ -173,6 +173,12 @@ untyped_header!(Privacy, "Privacy", Header::Privacy); untyped_header!(Path, "Path", Header::Path); untyped_header!(Identity, "Identity", Header::Identity); +impl std::convert::From for ReferTo { + fn from(uri: crate::sip::Uri) -> Self { + Self(format!("<{}>", uri)) + } +} + #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct CallId(pub String); impl CallId {