Skip to content

Commit fcef15c

Browse files
committed
bump version to 0.4.5 and enhance Record-Route handling to support comma-separated lists
1 parent e7fcf3a commit fcef15c

3 files changed

Lines changed: 55 additions & 4 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rsipstack"
3-
version = "0.4.4"
3+
version = "0.4.5"
44
edition = "2021"
55
description = "SIP Stack Rust library for building SIP applications"
66
license = "MIT"

src/transaction/message.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,11 +260,14 @@ impl EndpointInner {
260260
}
261261
}
262262
}
263-
// update route set from Record-Route header
263+
// update route set from Record-Route header (support comma-separated lists)
264264
let mut route_set = Vec::new();
265265
for header in resp.headers.iter() {
266266
if let Header::RecordRoute(record_route) = header {
267-
route_set.push(Header::Route(Route::from(record_route.value())));
267+
let value = record_route.value();
268+
for token in value.split(',').map(|t| t.trim()).filter(|t| !t.is_empty()) {
269+
route_set.push(Header::Route(Route::from(token)));
270+
}
268271
}
269272
}
270273
route_set.reverse();

src/transaction/tests/test_client.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::rsip_ext::RsipResponseExt;
1+
use crate::rsip_ext::{destination_from_request, RsipResponseExt};
22
use crate::transaction::key::{TransactionKey, TransactionRole};
33
use crate::transaction::transaction::Transaction;
44
use crate::transport::udp::UdpConnection;
@@ -167,6 +167,54 @@ Content-Length: 0\r\n\r\n";
167167
Ok(())
168168
}
169169

170+
#[tokio::test]
171+
async fn test_make_ack_accepts_comma_separated_record_route() -> Result<()> {
172+
let endpoint = super::create_test_endpoint(None).await?;
173+
174+
let raw_response = "SIP/2.0 200 OK\r\n\
175+
Via: SIP/2.0/UDP uac.example.com:5060;branch=z9hG4bK1\r\n\
176+
Record-Route: <sip:3.4.5.6:24364;r2=on;lr;ftag=Xq5kaQn5;did=9e8.3362>,<sip:3.4.5.6:21827;r2=on;lr;ftag=Xq5kaQn5;did=9e8.3362>,<sip:1.2.40.7:21827;lr;ftag=Xq5kaQn5;did=9e8.ab21>\r\n\
177+
From: <sip:alice@example.com>;tag=from-tag\r\n\
178+
To: <sip:bob@example.com>;tag=to-tag\r\n\
179+
Call-ID: callid@example.com\r\n\
180+
CSeq: 1 INVITE\r\n\
181+
Contact: <sip:uas@192.0.2.55:5080;transport=udp>\r\n\
182+
Content-Length: 0\r\n\r\n";
183+
184+
let response = Response::try_from(raw_response)?;
185+
let request_uri = response.remote_uri(None)?;
186+
let ack = endpoint.inner.make_ack(&response, request_uri)?;
187+
188+
let routes: Vec<String> = ack
189+
.headers
190+
.iter()
191+
.filter_map(|header| match header {
192+
Header::Route(route) => Some(route.value().to_string()),
193+
_ => None,
194+
})
195+
.collect();
196+
197+
assert_eq!(
198+
routes,
199+
vec![
200+
"<sip:1.2.40.7:21827;lr;ftag=Xq5kaQn5;did=9e8.ab21>".to_string(),
201+
"<sip:3.4.5.6:21827;r2=on;lr;ftag=Xq5kaQn5;did=9e8.3362>".to_string(),
202+
"<sip:3.4.5.6:24364;r2=on;lr;ftag=Xq5kaQn5;did=9e8.3362>".to_string(),
203+
],
204+
"ACK Route headers must follow the reversed Record-Route order"
205+
);
206+
207+
let destination =
208+
destination_from_request(&ack).expect("route-enabled ACK should resolve to a destination");
209+
let expected_destination = Uri::try_from("sip:1.2.40.7:21827;lr;ftag=Xq5kaQn5;did=9e8.ab21")?;
210+
assert_eq!(
211+
&*destination, &expected_destination,
212+
"First Route entry must determine the transport destination",
213+
);
214+
215+
Ok(())
216+
}
217+
170218
#[tokio::test]
171219
async fn test_make_ack_uses_contact_with_ob() -> Result<()> {
172220
let endpoint = super::create_test_endpoint(None).await?;

0 commit comments

Comments
 (0)