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 {
#[derive(Debug)]
struct PartialMessage {
- headers: Vec<Header>,
+ headers: Vec<(Header, UnparsedLine)>,
top_line: UnparsedLine,
}
-> 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(),
})
}
#[derive(Debug)]
struct Response {
status_line: (StatusLine, UnparsedLine),
- headers: Vec<Header>,
+ headers: Vec<(Header, UnparsedLine)>,
body: Option<BytesMut>,
}
#[derive(Debug)]
struct Request {
request_line: (RequestLine, UnparsedLine),
- headers: Vec<Header>,
+ headers: Vec<(Header, UnparsedLine)>,
body: Option<BytesMut>,
}
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
+ }
}
}
// 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))
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'\'' ||
);
);
+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));
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),
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'&' ||
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)
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 {
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) }
+));