sip_codec/
parser.rs

1use std::str;
2use std::string::FromUtf8Error;
3
4use failure::Error;
5use http::header::{HeaderMap, HeaderName, HeaderValue};
6use nom::{is_digit, is_space, rest, IResult};
7
8use crate::headers::content_length::ContentLength;
9use crate::headers::HeaderMapParse;
10use crate::request::Request;
11use crate::Version;
12
13mod header;
14
15fn is_newline(ch: u8) -> bool {
16	ch == b'\r' || ch == b'\n'
17}
18
19fn slice_to_string(slice: &[u8]) -> Result<String, FromUtf8Error> {
20	String::from_utf8(Vec::from(slice))
21}
22
23fn parse_u8(slice: &[u8]) -> Result<u8, Error> {
24	Ok(str::from_utf8(slice)?.parse()?)
25}
26
27enum BodyLength {
28	All,
29	Length(usize),
30}
31
32fn get_body_length(map: &HeaderMap) -> BodyLength {
33	map.typed_get::<ContentLength>()
34		.map(Into::into)
35		.map(BodyLength::Length)
36		.unwrap_or(BodyLength::All)
37}
38
39fn body(input: &[u8], length: BodyLength) -> IResult<&[u8], Vec<u8>> {
40	match length {
41		BodyLength::All => rest(input),
42		BodyLength::Length(len) => take!(input, len),
43	}
44	.map(|(rem, res)| (rem, res.into()))
45}
46
47named!(lf, alt!(tag!("\r\n") | tag!("\r") | tag!("\n")));
48
49named!(
50	headers<HeaderMap>,
51	map!(
52		many0!(complete!(terminated!(header::header, lf))),
53		make_header_map
54	)
55);
56
57named!(
58	version<Version>,
59	do_parse!(
60		tag!("SIP/")
61			>> major: map_res!(take_while1!(is_digit), parse_u8)
62			>> char!('.')
63			>> minor: map_res!(take_while1!(is_digit), parse_u8)
64			>> (Version { major, minor })
65	)
66);
67
68named!(
69	string_until_space<String>,
70	map_res!(take_till1!(is_space), slice_to_string)
71);
72
73named!(pub parse_request<&[u8], Request>,
74	do_parse!(
75		many0!(lf) >>
76 		method: string_until_space >>
77 		take_while1!(is_space) >>
78 		uri: string_until_space >>
79 		take_while1!(is_space) >>
80 		sip_version: version >>
81 		lf >>
82 		headers: headers >>
83 		lf >>
84 		body: call!(body, get_body_length(&headers)) >>
85        (RequestBuilder {method, uri, sip_version, headers, body: body}.into())
86	)
87);
88
89struct RequestBuilder {
90	pub method: String,
91	pub uri: String,
92	pub sip_version: Version,
93	pub headers: HeaderMap,
94	pub body: Vec<u8>,
95}
96
97impl Into<Request> for RequestBuilder {
98	fn into(self) -> Request {
99		Request {
100			method: self.method,
101			uri: self.uri,
102			sip_version: self.sip_version,
103			headers: self.headers,
104			body: self.body,
105		}
106	}
107}
108
109fn make_header_map(headers: Vec<(HeaderName, HeaderValue)>) -> HeaderMap {
110	let mut map = HeaderMap::new();
111	for (name, value) in headers {
112		map.insert(name, value);
113	}
114	map
115}
116
117#[cfg(test)]
118mod tests {
119	use super::*;
120
121	#[test]
122	fn no_space() {
123		let mut headers = HeaderMap::new();
124		headers.insert("a", HeaderValue::from_static("b"));
125		headers.insert("content-length", HeaderValue::from_static("7"));
126
127		let expected = Ok((
128			&[][..],
129			Request {
130				method: "GET".into(),
131				uri: "sip:user@server:port".into(),
132				sip_version: Default::default(),
133				headers,
134				body: b"abcdefg".to_vec(),
135			},
136		));
137		assert_eq!(
138			expected,
139			parse_request(
140				b"GET sip:user@server:port SIP/2.0\r\na:b\r\nContent-length: 7\r\n\r\nabcdefg"
141			)
142		);
143	}
144
145	#[test]
146	fn shorthand() {
147		let mut headers = HeaderMap::new();
148		headers.insert("a", HeaderValue::from_static("b"));
149		headers.insert("l", HeaderValue::from_static("7"));
150
151		let expected = Ok((
152			&b"h"[..],
153			Request {
154				method: "GET".into(),
155				uri: "sip:user@server:port".into(),
156				sip_version: Default::default(),
157				headers,
158				body: b"abcdefg".to_vec(),
159			},
160		));
161		assert_eq!(
162			expected,
163			parse_request(b"GET sip:user@server:port SIP/2.0\r\na:b\r\nl: 7\r\n\r\nabcdefgh")
164		);
165	}
166}