From: Richard Whitehouse Date: Sun, 29 Oct 2017 10:49:04 +0000 (+0000) Subject: Parse headers correctly X-Git-Url: https://git.richardwhiuk.com/?a=commitdiff_plain;h=650ae57f21240ae12cbbbd5d8cd86b1f02db3ace;p=rust-sip.git Parse headers correctly Only supports Accept Header currently --- diff --git a/src/codec.rs b/src/codec.rs index 94b1c23..d4dead4 100644 --- a/src/codec.rs +++ b/src/codec.rs @@ -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 { - 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
, + headers: Vec<(Header, UnparsedLine)>, top_line: UnparsedLine, } @@ -231,7 +216,7 @@ impl PartialMessage { -> Result { 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
, + headers: Vec<(Header, UnparsedLine)>, body: Option, } @@ -264,7 +249,7 @@ impl Response { #[derive(Debug)] struct Request { request_line: (RequestLine, UnparsedLine), - headers: Vec
, + headers: Vec<(Header, UnparsedLine)>, body: Option, } @@ -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)) diff --git a/src/parser.rs b/src/parser.rs index 13c46ad..081d7c4 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -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, 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>, many1!(take_1!(is_token))); + +named!(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(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::() { + 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, 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, 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>, many1!(alt!( take_1!(is_param_unreserved) | escaped))); -fn number(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::() { - 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, alt!( preceded!(tag!("transport="), transport_param) => { |transport| UriParameter::Transport(transport) @@ -262,34 +263,36 @@ named!(hvalue<&[u8], Vec>, 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, HostPort, Vec, Option); -named!(sip_uri<&[u8], (Option, HostPort, Vec, Option)>, 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, HostPort, Vec, Option)>, 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, alt!( code: c, reason: r })})); + +/// Headers + +// Accept Header + +named!(media_type, 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, take_1!(is_qdtext_char)); + +fn is_qdpair_char(c: u8) -> bool { + c <= 0x09 || c == 0x0B || c == 0x0C || (c >= 0x0E && c <= 0x7f) +} + +named!(qdpair, preceded!( + tag!("\\"), + take_1!(is_qdpair_char))); + +named!(quoted_string>, delimited!( + tag!(b"\""), + many0!(alt!( + qdtext | + qdpair)), + tag!(b"\""))); + +named!(media_value>, alt!( + token | + quoted_string)); + +named!(media_parameter, separated_pair!( + media_attribute, + tag!(b"="), + media_value)); + +named!(media_range, 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, separated_pair!( + token, + tag!(b"="), + opt!(token))); + +use nom::float as qvalue; + +named!(accept_param, alt!( + preceded!(tag!(b"q"), qvalue) => { |x| AcceptParam::Qvalue(x) } | + generic_param => { |x| AcceptParam::Generic(x) })); + +named!(accept_range, tuple!( + media_range, + many0!(preceded!( + tag!(b";"), + accept_param)))); + +named!(accept_header>, preceded!( + tag!("Accept:"), + separated_nonempty_list!( + tag!(b","), + accept_range))); + +named!(pub header
, alt!( +// RFC 3261 Headers + accept_header => { |a| Header::Accept(a) } +)); diff --git a/src/types.rs b/src/types.rs index b1b58ea..6135abf 100644 --- a/src/types.rs +++ b/src/types.rs @@ -44,9 +44,9 @@ pub enum UriPath { pub type Scheme = (u8, Vec); -pub type Header = (Vec, Vec); +pub type UriHeader = (Vec, Vec); -pub type Headers = (Header, Vec
); +pub type UriHeaders = (UriHeader, Vec); #[derive(Debug)] pub enum Transport { @@ -98,7 +98,7 @@ pub struct SipUri { pub user_info: Option, pub host_port: HostPort, pub params: Vec, - pub headers: Option, + pub headers: Option, } #[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), +} + +#[derive(Debug)] +pub enum MediaFullType { + All, + Partial(MediaType), + Specific(MediaType, Vec), +} + +pub type MediaParameter = (Vec, Vec); + +pub type MediaRange = (MediaFullType, Vec); + +pub type GenericParam = (Vec, Option>); + +#[derive(Debug)] +pub enum AcceptParam { + Qvalue(f32), + Generic(GenericParam), +} + +pub type AcceptRange = (MediaRange, Vec); + +#[derive(Debug)] +pub enum Header { + Accept(Vec), + From(Uri), + To(Uri), + Extension { name: String, value: String }, +}