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;
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,
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)?;
}
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)?;
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((_, _)) => {
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;
}
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]
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());
}
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>"
+ );
+ }
}