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}