rust/sip: store multiple header values

According to RFC 3261, a single header can be repeated one or more times,
and its name can also be specified using the 'compact form.'

This patch updates the hashmap used for storing headers to accommodate multiple
values instead of just one.

Additionally, if a header name is defined in the compact form, it is expanded
into its long form (i.e., the standard name).

This conversion simplifies the logic for matching a given header
and ensures 1:1 parity with keywords.

Ticket #6374
pull/11809/head
Giuseppe Longo 4 months ago committed by Victor Julien
parent 969f4d131f
commit cfb793ce28

@ -21,7 +21,7 @@ use crate::sdp::parser::{sdp_parse_message, SdpMessage};
use nom7::bytes::streaming::{tag, take, take_while, take_while1};
use nom7::character::streaming::{char, crlf};
use nom7::character::{is_alphabetic, is_alphanumeric, is_digit, is_space};
use nom7::combinator::{map_res, opt};
use nom7::combinator::{map, map_res, opt};
use nom7::sequence::delimited;
use nom7::{Err, IResult, Needed};
use std;
@ -38,7 +38,7 @@ pub struct Request {
pub method: String,
pub path: String,
pub version: String,
pub headers: HashMap<String, String>,
pub headers: HashMap<String, Vec<String>>,
pub request_line_len: u16,
pub headers_len: u16,
@ -97,6 +97,22 @@ fn is_header_value(b: u8) -> bool {
is_alphanumeric(b) || is_token_char(b) || b"\"#$&(),/;:<=>?@[]{}()^|~\\\t\n\r ".contains(&b)
}
fn expand_header_name(h: &str) -> &str {
match h {
"i" => "Call-ID",
"m" => "Contact",
"e" => "Content-Encoding",
"l" => "Content-Length",
"c" => "Content-Type",
"f" => "From",
"s" => "Subject",
"k" => "Supported",
"t" => "To",
"v" => "Via",
_ => h,
}
}
pub fn sip_parse_request(oi: &[u8]) -> IResult<&[u8], Request> {
let (i, method) = parse_method(oi)?;
let (i, _) = char(' ')(i)?;
@ -199,7 +215,7 @@ fn hcolon(i: &[u8]) -> IResult<&[u8], char> {
}
fn message_header(i: &[u8]) -> IResult<&[u8], Header> {
let (i, n) = header_name(i)?;
let (i, n) = map(header_name, expand_header_name)(i)?;
let (i, _) = hcolon(i)?;
let (i, v) = header_value(i)?;
let (i, _) = crlf(i)?;
@ -217,8 +233,8 @@ pub fn sip_take_line(i: &[u8]) -> IResult<&[u8], Option<String>> {
Ok((i, Some(line.into())))
}
pub fn parse_headers(mut input: &[u8]) -> IResult<&[u8], HashMap<String, String>> {
let mut headers_map: HashMap<String, String> = HashMap::new();
pub fn parse_headers(mut input: &[u8]) -> IResult<&[u8], HashMap<String, Vec<String>>> {
let mut headers_map: HashMap<String, Vec<String>> = HashMap::new();
loop {
match crlf(input) as IResult<&[u8], _> {
Ok((_, _)) => {
@ -229,7 +245,10 @@ pub fn parse_headers(mut input: &[u8]) -> IResult<&[u8], HashMap<String, String>
Err(Err::Incomplete(e)) => return Err(Err::Incomplete(e)),
};
let (rest, header) = message_header(input)?;
headers_map.insert(header.name, header.value);
headers_map
.entry(header.name)
.or_default()
.push(header.value);
input = rest;
}
@ -292,7 +311,7 @@ mod tests {
assert_eq!(req.method, "REGISTER");
assert_eq!(req.path, "sip:sip.cybercity.dk");
assert_eq!(req.version, "SIP/2.0");
assert_eq!(req.headers["Content-Length"], "0");
assert_eq!(req.headers["Content-Length"].first().unwrap(), "0");
}
#[test]
@ -308,7 +327,7 @@ mod tests {
assert_eq!(req.method, "REGISTER");
assert_eq!(req.path, "sip:sip.cybercity.dk");
assert_eq!(req.version, "SIP/2.0");
assert_eq!(req.headers["Content-Length"], "4");
assert_eq!(req.headers["Content-Length"].first().unwrap(), "4");
assert_eq!(body, "ABCD".as_bytes());
}
@ -339,4 +358,28 @@ mod tests {
let (_rem, result) = parse_version(buf).unwrap();
assert_eq!(result, "SIP/2.0");
}
#[test]
fn test_header_multi_value() {
let buf: &[u8] = "REGISTER sip:sip.cybercity.dk SIP/2.0\r\n\
From: <sip:voi18063@sip.cybercity.dk>;tag=903df0a\r\n\
To: <sip:voi18063@sip.cybercity.dk>\r\n\
Route: <sip:bob@biloxi.com>\r\n\
Route: <sip:carol@chicago.com>\r\n\
\r\n"
.as_bytes();
let (_, req) = sip_parse_request(buf).unwrap();
assert_eq!(req.method, "REGISTER");
assert_eq!(req.path, "sip:sip.cybercity.dk");
assert_eq!(req.version, "SIP/2.0");
assert_eq!(
req.headers["Route"].first().unwrap(),
"<sip:bob@biloxi.com>"
);
assert_eq!(
req.headers["Route"].get(1).unwrap(),
"<sip:carol@chicago.com>"
);
}
}

Loading…
Cancel
Save