Parse headers correctly
authorRichard Whitehouse <github@richardwhiuk.com>
Sun, 29 Oct 2017 10:49:04 +0000 (10:49 +0000)
committerRichard Whitehouse <github@richardwhiuk.com>
Sun, 29 Oct 2017 10:49:04 +0000 (10:49 +0000)
Only supports Accept Header currently

src/codec.rs
src/parser.rs
src/types.rs

index 94b1c233ab03f7eb767a620320d29957a3591cf0..d4dead4cace162c228fe92c722b1bfaa53839b55 100644 (file)
@@ -10,42 +10,27 @@ use std::io;
 use std;
 use nom;
 
-use types::{RequestLine, StatusLine, TopLine, Uri};
+use types::{RequestLine, StatusLine, TopLine, Header};
 use parser::top_line;
+use parser::header;
 
 const SPACE: u8 = b' ';
 const TAB: u8 = b'\t';
 const LF: u8 = b'\n';
 const CR: u8 = b'\r';
 
-#[derive(Debug)]
-struct Header {
-    key: String,
-    raw_value: UnparsedLine,
-    dirty: bool,
-    parsed: HeaderType,
-}
-
 impl Header {
-    fn parse(line: UnparsedLine) -> Result<Header, std::str::Utf8Error> {
-        Ok(Header {
-            key: "To".to_string(),
-            raw_value: line,
-            dirty: false,
-            parsed: HeaderType::Extension {
-                name: "To".to_string(),
-                value: "".to_string(),
-            },
-        })
+    fn parse(line: UnparsedLine) -> Option<(Header, UnparsedLine)> {
+        match header(line.as_bytes()) {
+            nom::IResult::Done(_, h) => Some((h, line.clone())),
+            result => {
+                println!("Failed to parse header: {:?} - {:?}", line, result);
+                None
+            }
+        }
     }
 }
 
-#[derive(Debug)]
-enum HeaderType {
-    From { uri: Uri },
-    To { uri: Uri },
-    Extension { name: String, value: String },
-}
 
 #[derive(Clone, Debug)]
 struct UnparsedLine {
@@ -221,7 +206,7 @@ impl SipCodec {
 
 #[derive(Debug)]
 struct PartialMessage {
-    headers: Vec<Header>,
+    headers: Vec<(Header, UnparsedLine)>,
     top_line: UnparsedLine,
 }
 
@@ -231,7 +216,7 @@ impl PartialMessage {
            -> Result<PartialMessage, std::str::Utf8Error> {
         Ok(PartialMessage {
             top_line: top_line,
-            headers: try!(headers.drain(..).map(|line| Header::parse(line)).collect()),
+            headers: headers.drain(..).filter_map(|line| Header::parse(line)).collect(),
         })
     }
 
@@ -251,7 +236,7 @@ enum Message {
 #[derive(Debug)]
 struct Response {
     status_line: (StatusLine, UnparsedLine),
-    headers: Vec<Header>,
+    headers: Vec<(Header, UnparsedLine)>,
     body: Option<BytesMut>,
 }
 
@@ -264,7 +249,7 @@ impl Response {
 #[derive(Debug)]
 struct Request {
     request_line: (RequestLine, UnparsedLine),
-    headers: Vec<Header>,
+    headers: Vec<(Header, UnparsedLine)>,
     body: Option<BytesMut>,
 }
 
@@ -287,15 +272,18 @@ impl Message {
                     request_line: (r, message.top_line.clone()),
                     body: body,
                 }))
-            },
+            }
             nom::IResult::Done(_, TopLine::StatusLine(s)) => {
                 Some(Message::Response(Response {
                     headers: message.headers,
                     status_line: (s, message.top_line.clone()),
                     body: body,
                 }))
-            },
-            result => { println!("Failed to parse top line: {:?}", result); None },
+            }
+            result => {
+                println!("Failed to parse top line: {:?}", result);
+                None
+            }
         }
     }
 
@@ -436,10 +424,12 @@ mod tests {
         // Send a request
         let socket = TcpStream::connect(&addr, &handle);
 
-        let request = socket.and_then(|socket| {
-            io::write_all(socket,
-                          "MESSAGE sip:test.com SIP/2.0\r\nVia: localhost\r\n\r\n")
-        });
+        let request =
+            socket.and_then(|socket| {
+                io::write_all(socket,
+                              "MESSAGE sip:test.com SIP/2.0\r\nAccept:text/plain\r\nVia: \
+                               localhost\r\n\r\n")
+            });
 
         let finished = request.and_then(|(socket, _request)| {
             futures::done(socket.shutdown(Shutdown::Write).map(|_| socket))
index 13c46add1dbeb03c0c100caf965a2fc7207bbf3f..081d7c497c4cd6b07bb9e4691a5a8c6732257457 100644 (file)
@@ -8,32 +8,10 @@ use nom::alphanumeric as alphanumerics;
 use nom::IResult;
 use nom::ErrorKind::Custom;
 
-use types::{PathSegment, HostPort, Host, Hostname, UriParameter, Header, Headers, SipUri, Uri,
-            HierarchicalPath, Authority, UriPath, UserInfo, AbsolutePath, Scheme, Method,
-            Transport, UserParam, Version, RequestLine, StatusLine, TopLine};
-
-fn is_token(c: u8) -> bool {
-    nom::is_alphanumeric(c) || c == b'-' || c == b'.' || c == b'!' || c == b'%' ||
-    c == b'*' || c == b'_' || c == b'+' || c == b'`' || c == b'\'' || c == b'~'
-}
-
-named!(token, take_while!(is_token));
-
-named!(method<Method>, alt!(
-// RFC 3261
-    tag!("INVITE") => { |_|  Method::INVITE } |
-    tag!("ACK") => { |_|  Method::ACK } |
-    tag!("OPTIONS") => { |_|  Method::OPTIONS } |
-    tag!("BYE") => { |_|  Method::BYE } |
-    tag!("CANCEL") => { |_|  Method::CANCEL } |
-    tag!("REGISTER") => { |_|  Method::REGISTER } |
-// Extensions
-    tag!("MESSAGE") => { |_|  Method::MESSAGE } |
-    tag!("REFER") => { |_|  Method::REFER } |
-    tag!("SUBSCRIBE") => { |_|  Method::SUBSCRIBE } |
-    tag!("NOTIFY") => { |_|  Method::NOTIFY } |
-    token  => { |method : &[u8]| Method::Extension(method.to_vec()) }
-));
+use types::{PathSegment, HostPort, Host, Hostname, UriParameter, UriHeader, UriHeaders, SipUri,
+            Uri, HierarchicalPath, Authority, UriPath, UserInfo, AbsolutePath, Scheme, Method,
+            Transport, UserParam, Version, RequestLine, StatusLine, TopLine, Header, MediaType,
+            MediaFullType, MediaParameter, MediaRange, GenericParam, AcceptParam, AcceptRange};
 
 fn is_mark(c: u8) -> bool {
     c == b'-' || c == b'_' || c == b'.' || c == b'!' || c == b'~' || c == b'*' || c == b'\'' ||
@@ -94,6 +72,29 @@ macro_rules! take_1 (
   );
 );
 
+fn is_token(c: u8) -> bool {
+    nom::is_alphanumeric(c) || c == b'-' || c == b'.' || c == b'!' || c == b'%' ||
+    c == b'*' || c == b'_' || c == b'+' || c == b'`' || c == b'\'' || c == b'~'
+}
+
+named!(token<Vec<u8>>, many1!(take_1!(is_token)));
+
+named!(method<Method>, alt!(
+// RFC 3261
+    tag!("INVITE") => { |_|  Method::INVITE } |
+    tag!("ACK") => { |_|  Method::ACK } |
+    tag!("OPTIONS") => { |_|  Method::OPTIONS } |
+    tag!("BYE") => { |_|  Method::BYE } |
+    tag!("CANCEL") => { |_|  Method::CANCEL } |
+    tag!("REGISTER") => { |_|  Method::REGISTER } |
+// Extensions
+    tag!("MESSAGE") => { |_|  Method::MESSAGE } |
+    tag!("REFER") => { |_|  Method::REFER } |
+    tag!("SUBSCRIBE") => { |_|  Method::SUBSCRIBE } |
+    tag!("NOTIFY") => { |_|  Method::NOTIFY } |
+    token  => { |method| Method::Extension(method) }
+));
+
 named!(alpha<&[u8], u8>, take_1!(nom::is_alphabetic));
 named!(alphanumeric<&[u8], u8>, take_1!(nom::is_alphanumeric));
 named!(digit<&[u8], u8>, take_1!(nom::is_digit));
@@ -159,6 +160,26 @@ named!(hostname<&[u8], Hostname>, tuple!(
     many0!(terminated!(domain_label, tag!(b"."))),
     terminated!(top_label, opt!(tag!(b".")))));
 
+fn number<O>(input: &[u8]) -> IResult<&[u8], O>
+    where O: std::str::FromStr
+{
+    match digits(input) {
+        IResult::Done(left, num_str) => {
+            if let Ok(num_utf8) = std::str::from_utf8(num_str) {
+                if let Ok(num_i) = num_utf8.parse::<O>() {
+                    IResult::Done(left, num_i)
+                } else {
+                    IResult::Error(Custom(1))
+                }
+            } else {
+                IResult::Error(Custom(2))
+            }
+        }
+        IResult::Error(e) => IResult::Error(e),
+        IResult::Incomplete(i) => IResult::Incomplete(i),
+    }
+}
+
 named!(ipv4_address<&[u8], (u8, u8, u8, u8)>, tuple!(
     number,
     preceded!(tag!(b"."), number),
@@ -189,13 +210,13 @@ named!(transport_param<Transport>, alt!(
     tag!("udp") => { |_| Transport::Udp } |
     tag!("sctp") => { |_| Transport::Sctp } |
     tag!("tls") => { |_| Transport::Tls } |
-    token => { |token : &[u8]| Transport::Other(token.to_vec()) }
+    token => { |token| Transport::Other(token) }
 ));
 
 named!(user_param<UserParam>, alt!(
     tag!("phone") => { |_| UserParam::Phone } |
     tag!("ip") => { |_| UserParam::Ip } |
-    token => { |token : &[u8]| UserParam::Other(token.to_vec()) }));
+    token => { |token| UserParam::Other(token) }));
 
 fn is_param_unreserved(c: u8) -> bool {
     is_unreserved(c) || c == b'[' || c == b']' || c == b'/' || c == b':' || c == b'&' ||
@@ -206,26 +227,6 @@ named!(param<&[u8], Vec<u8>>, many1!(alt!(
     take_1!(is_param_unreserved) |
     escaped)));
 
-fn number<O>(input: &[u8]) -> IResult<&[u8], O>
-    where O: std::str::FromStr
-{
-    match digits(input) {
-        IResult::Done(left, num_str) => {
-            if let Ok(num_utf8) = std::str::from_utf8(num_str) {
-                if let Ok(num_i) = num_utf8.parse::<O>() {
-                    IResult::Done(left, num_i)
-                } else {
-                    IResult::Error(Custom(1))
-                }
-            } else {
-                IResult::Error(Custom(2))
-            }
-        }
-        IResult::Error(e) => IResult::Error(e),
-        IResult::Incomplete(i) => IResult::Incomplete(i),
-    }
-}
-
 named!(uri_parameter<UriParameter>, alt!(
     preceded!(tag!("transport="), transport_param) => {
        |transport| UriParameter::Transport(transport)
@@ -262,34 +263,36 @@ named!(hvalue<&[u8], Vec<u8>>, many1!(alt!(
     take_1!(is_header_unreserved) |
     escaped)));
 
-named!(header<&[u8], Header>, separated_pair!(
+named!(uri_header<&[u8], UriHeader>, separated_pair!(
     hname,
     tag!("="),
     hvalue));
 
-named!(headers<&[u8], Headers>, preceded!(
+named!(uri_headers<&[u8], UriHeaders>, preceded!(
     char!('?'),
     tuple!(
-        header,
+        uri_header,
         many0!(
-            preceded!(char!('&'), header)))));
+            preceded!(char!('&'), uri_header)))));
+
+type _SipUri = (Option<UserInfo>, HostPort, Vec<UriParameter>, Option<UriHeaders>);
 
-named!(sip_uri<&[u8], (Option<UserInfo>, HostPort, Vec<UriParameter>, Option<Headers>)>, preceded!(
+named!(sip_uri<&[u8], _SipUri>, preceded!(
     tag!("sip:"),
     tuple!(
         opt!(userinfo),
         hostport,
         uri_parameters,
-        opt!(headers)
+        opt!(uri_headers)
         )));
 
-named!(sips_uri<&[u8], (Option<UserInfo>, HostPort, Vec<UriParameter>, Option<Headers>)>, preceded!(
+named!(sips_uri<&[u8], _SipUri>, preceded!(
     tag!("sips:"),
     tuple!(
         opt!(userinfo),
         hostport,
         uri_parameters,
-        opt!(headers)
+        opt!(uri_headers)
         )));
 
 fn is_scheme_unreserved(c: u8) -> bool {
@@ -434,3 +437,94 @@ named!(pub top_line<TopLine>, alt!(
                code: c,
                reason: r
        })}));
+
+/// Headers
+
+// Accept Header
+
+named!(media_type<MediaType>, alt!(
+// Discrete Types
+       tag!(b"text") => { |_| MediaType::Text } |
+       tag!(b"image") => { |_| MediaType::Image } |
+       tag!(b"audio") => { |_| MediaType::Audio } |
+       tag!(b"video") => { |_| MediaType::Video } |
+       tag!(b"application") => { |_| MediaType::Application } |
+// Composite Types
+       tag!(b"message") => { |_| MediaType::Message } |
+       tag!(b"multipart") => { |_| MediaType::Multipart } |
+// Extensions
+       token => { |x| MediaType::Extension(x) }));
+
+// We don't bother splitting IANA, IETF and x- token types.
+use self::token as media_subtype;
+
+use self::token as media_attribute;
+
+fn is_qdtext_char(c: u8) -> bool {
+    c == 0x21 || (c >= 0x23 && c <= 0x5b) || (c >= 0x5d && c <= 0x7e)
+}
+
+named!(qdtext<u8>, take_1!(is_qdtext_char));
+
+fn is_qdpair_char(c: u8) -> bool {
+    c <= 0x09 || c == 0x0B || c == 0x0C || (c >= 0x0E && c <= 0x7f)
+}
+
+named!(qdpair<u8>, preceded!(
+       tag!("\\"),
+       take_1!(is_qdpair_char)));
+
+named!(quoted_string<Vec<u8>>, delimited!(
+       tag!(b"\""),
+       many0!(alt!(
+               qdtext |
+               qdpair)),
+       tag!(b"\"")));
+
+named!(media_value<Vec<u8>>, alt!(
+       token |
+       quoted_string));
+
+named!(media_parameter<MediaParameter>, separated_pair!(
+       media_attribute,
+       tag!(b"="),
+       media_value));
+
+named!(media_range<MediaRange>, tuple!(
+       alt!(
+               tag!(b"*/*") => { |_| MediaFullType::All } |
+               terminated!(media_type, tag!(b"/*")) => { |x| MediaFullType::Partial(x) } | /**/
+               separated_pair!(
+                       media_type,
+                       tag!(b"/"),
+                       media_subtype) => { |(t, st)| MediaFullType::Specific(t, st) }),
+       many0!(
+               preceded!(tag!(b";"), media_parameter))));
+
+named!(generic_param<GenericParam>, separated_pair!(
+       token,
+       tag!(b"="),
+       opt!(token)));
+
+use nom::float as qvalue;
+
+named!(accept_param<AcceptParam>, alt!(
+       preceded!(tag!(b"q"), qvalue) => { |x| AcceptParam::Qvalue(x) } |
+       generic_param => { |x| AcceptParam::Generic(x) }));
+
+named!(accept_range<AcceptRange>, tuple!(
+       media_range,
+       many0!(preceded!(
+               tag!(b";"),
+               accept_param))));
+
+named!(accept_header<Vec<AcceptRange>>, preceded!(
+       tag!("Accept:"),
+       separated_nonempty_list!(
+               tag!(b","),
+               accept_range)));
+
+named!(pub header<Header>, alt!(
+// RFC 3261 Headers
+       accept_header => { |a| Header::Accept(a) }
+));
index b1b58ea76d07a6ba40d06687bd1dcb0239d0f195..6135abff1adf2325b8e8a131bad9f7ed3848b83e 100644 (file)
@@ -44,9 +44,9 @@ pub enum UriPath {
 
 pub type Scheme = (u8, Vec<u8>);
 
-pub type Header = (Vec<u8>, Vec<u8>);
+pub type UriHeader = (Vec<u8>, Vec<u8>);
 
-pub type Headers = (Header, Vec<Header>);
+pub type UriHeaders = (UriHeader, Vec<UriHeader>);
 
 #[derive(Debug)]
 pub enum Transport {
@@ -98,7 +98,7 @@ pub struct SipUri {
     pub user_info: Option<UserInfo>,
     pub host_port: HostPort,
     pub params: Vec<UriParameter>,
-    pub headers: Option<Headers>,
+    pub headers: Option<UriHeaders>,
 }
 
 #[derive(Debug)]
@@ -131,3 +131,44 @@ pub enum TopLine {
     RequestLine(RequestLine),
     StatusLine(StatusLine),
 }
+
+#[derive(Debug)]
+pub enum MediaType {
+    Text,
+    Image,
+    Audio,
+    Video,
+    Application,
+    Message,
+    Multipart,
+    Extension(Vec<u8>),
+}
+
+#[derive(Debug)]
+pub enum MediaFullType {
+    All,
+    Partial(MediaType),
+    Specific(MediaType, Vec<u8>),
+}
+
+pub type MediaParameter = (Vec<u8>, Vec<u8>);
+
+pub type MediaRange = (MediaFullType, Vec<MediaParameter>);
+
+pub type GenericParam = (Vec<u8>, Option<Vec<u8>>);
+
+#[derive(Debug)]
+pub enum AcceptParam {
+    Qvalue(f32),
+    Generic(GenericParam),
+}
+
+pub type AcceptRange = (MediaRange, Vec<AcceptParam>);
+
+#[derive(Debug)]
+pub enum Header {
+    Accept(Vec<AcceptRange>),
+    From(Uri),
+    To(Uri),
+    Extension { name: String, value: String },
+}