--- /dev/null
+use bytes::BytesMut;
+
+use futures::future;
+use futures::{Future, Stream};
+use tokio_io::AsyncRead;
+use tokio_core::reactor::Handle;
+use tokio_core::net::TcpListener;
+use tokio_io::codec::{Encoder, Decoder};
+use std::io;
+use std;
+use nom;
+
+use types::{RequestLine, StatusLine, TopLine, Uri};
+use parser::top_line;
+
+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(),
+ },
+ })
+ }
+}
+
+#[derive(Debug)]
+enum HeaderType {
+ From { uri: Uri },
+ To { uri: Uri },
+ Extension { name: String, value: String },
+}
+
+#[derive(Clone, Debug)]
+struct UnparsedLine {
+ value: BytesMut,
+}
+
+impl UnparsedLine {
+ fn new(value: BytesMut) -> UnparsedLine {
+ UnparsedLine { value: value }
+ }
+
+ fn add(&mut self, value: &BytesMut) {
+ self.value.extend_from_slice(value);
+ }
+
+ fn as_bytes(&self) -> &[u8] {
+ &self.value
+ }
+}
+
+struct SipCodec {
+ message: Option<PartialMessage>,
+ headers: Vec<UnparsedLine>,
+ top_line: Option<UnparsedLine>,
+ latest_header: Option<UnparsedLine>,
+}
+
+impl SipCodec {
+ fn new() -> SipCodec {
+ SipCodec {
+ message: None,
+ headers: Vec::new(),
+ top_line: None,
+ latest_header: None,
+ }
+ }
+
+ fn parse_message(&mut self, buf: &mut BytesMut) -> io::Result<Option<Message>> {
+ loop {
+ println!("Gathering headers from {:?}", std::str::from_utf8(&buf));
+
+ let mut done = false;
+ let mut total = 0;
+
+ {
+ let mut search: Box<Iterator<Item = &u8>> = Box::new(buf.iter());
+
+ while !done {
+ println!("Gathering CRLF");
+
+ if let Some(cr) = search.position(|&b| b == CR) {
+
+ println!("Found CR at {}", cr);
+
+ total += cr;
+
+ let next = search.next();
+
+ println!("Got {:?} after CR", next);
+
+ if let Some(&LF) = next {
+ done = true;
+ }
+ } else {
+ return Ok(None);
+ }
+ }
+ }
+
+ // We've got a line terminating in \r\n.
+ let line = buf.split_to(total + 2);
+
+ println!("Using line: {:?}", std::str::from_utf8(&line));
+
+ match (&mut self.top_line, chartype(line.first()), &mut self.latest_header) {
+ // Ignore empty top lines
+ (&mut None, CharType::Line, _) => {
+
+ println!("Ignoring empty top line");
+
+ }
+
+ // Top line beginnning with whitespace - ignore
+ (&mut None, CharType::Whitespace, _) => {
+ println!("Got top line beginning with whitespace - ignore: {:?}",
+ line);
+ }
+
+ // Top line with no headers! Discard.
+ (top_line @ &mut None, CharType::Other, _) => {
+ println!("Got new top line of request: {:?}", line);
+
+ top_line.get_or_insert(UnparsedLine::new(line));
+ }
+
+ // Top line with no headers! Discard.
+ (top_line @ &mut Some(_), CharType::Line, &mut None) => {
+ println!("Got request with no headers - discarding");
+ top_line.take();
+ }
+
+ // Whitespace wrapped req line
+ (&mut Some(ref mut top_line), CharType::Whitespace, &mut None) => {
+ top_line.add(&line)
+ }
+
+ // Whitespace wrapped header
+ (&mut Some(_), CharType::Whitespace, &mut Some(ref mut latest_header)) => {
+ latest_header.add(&line)
+ }
+
+ // First header
+ (&mut Some(_), CharType::Other, latest_header @ &mut None) => {
+ latest_header.get_or_insert(UnparsedLine::new(line));
+ }
+
+ // New header
+ (&mut Some(_), CharType::Other, latest_header @ &mut Some(_)) => {
+ self.headers.push(latest_header.take().unwrap());
+ latest_header.get_or_insert(UnparsedLine::new(line));
+ }
+
+ // End of headers
+ (top_line @ &mut Some(_), CharType::Line, old_header @ &mut Some(_)) => {
+ self.headers.push(old_header.take().unwrap());
+
+ println!("Got end of headers");
+
+ if let Ok(new_message) = PartialMessage::new(top_line.take()
+ .unwrap(),
+ &mut self.headers) {
+
+ println!("Got partial message: {:?}", new_message);
+
+ self.headers.clear();
+
+ let length = new_message.body_length();
+
+ if length == 0 {
+ if let Some(message) = Message::new(new_message) {
+ println!("Got message without body: {:?}", message);
+
+ return Ok(Some(message));
+ } else {
+ println!("Failed to parse message with no body!");
+ }
+ } else if length > buf.len() {
+ self.message.get_or_insert(new_message);
+ return Ok(None);
+ } else {
+ let body = buf.split_to(length);
+ if let Some(message) = Message::new_with_body(new_message, body) {
+ println!("Got message with body: {:?}", message);
+
+ return Ok(Some(message));
+ } else {
+ println!("Failed to parse message with body!");
+ }
+ }
+ } else {
+ println!("Failed to parse partial message!");
+
+ // Message failed to parse
+ top_line.take();
+ self.headers.clear();
+ }
+ }
+ }
+ }
+
+ }
+}
+
+#[derive(Debug)]
+struct PartialMessage {
+ headers: Vec<Header>,
+ top_line: UnparsedLine,
+}
+
+impl PartialMessage {
+ fn new(top_line: UnparsedLine,
+ headers: &mut Vec<UnparsedLine>)
+ -> Result<PartialMessage, std::str::Utf8Error> {
+ Ok(PartialMessage {
+ top_line: top_line,
+ headers: try!(headers.drain(..).map(|line| Header::parse(line)).collect()),
+ })
+ }
+
+ fn body_length(&self) -> usize {
+ let body_length = 0;
+
+ body_length
+ }
+}
+
+#[derive(Debug)]
+enum Message {
+ Request(Request),
+ Response(Response),
+}
+
+#[derive(Debug)]
+struct Response {
+ status_line: (StatusLine, UnparsedLine),
+ headers: Vec<Header>,
+ body: Option<BytesMut>,
+}
+
+impl Response {
+ fn as_bytes(&self) -> &[u8] {
+ return self.status_line.1.as_bytes();
+ }
+}
+
+#[derive(Debug)]
+struct Request {
+ request_line: (RequestLine, UnparsedLine),
+ headers: Vec<Header>,
+ body: Option<BytesMut>,
+}
+
+impl Request {
+ fn as_bytes(&self) -> &[u8] {
+ return self.request_line.1.as_bytes();
+ }
+}
+
+impl Message {
+ fn new(message: PartialMessage) -> Option<Message> {
+ Message::parse(message, None)
+ }
+
+ fn parse(message: PartialMessage, body: Option<BytesMut>) -> Option<Message> {
+ match top_line(message.top_line.as_bytes()) {
+ nom::IResult::Done(_, TopLine::RequestLine(r)) => {
+ Some(Message::Request(Request {
+ headers: message.headers,
+ 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 },
+ }
+ }
+
+ fn new_with_body(message: PartialMessage, body: BytesMut) -> Option<Message> {
+ Message::parse(message, Some(body))
+ }
+
+ fn as_bytes<'a>(&'a self) -> &'a [u8] {
+ match self {
+ &Message::Request(ref r) => r.as_bytes(),
+ &Message::Response(ref r) => r.as_bytes(),
+ }
+ }
+}
+
+impl Decoder for SipCodec {
+ type Item = Message;
+ type Error = io::Error;
+
+ fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<Message>> {
+ if let Some(_) = self.message {
+
+ println!("Gathering body");
+
+ let length = self.message.iter().map(|m| m.body_length()).next().unwrap();
+
+ if length > buf.len() {
+ return Ok(None);
+ } else if let Some(message) = Message::new_with_body(self.message.take().unwrap(),
+ buf.split_to(length)) {
+ return Ok(Some(message));
+ } else {
+ println!("Failed to parse message with no body!");
+ }
+ }
+
+ self.parse_message(buf)
+ }
+}
+
+impl Encoder for SipCodec {
+ type Item = Message;
+ type Error = io::Error;
+
+ fn encode(&mut self, msg: Message, buf: &mut BytesMut) -> io::Result<()> {
+ buf.extend(msg.as_bytes());
+ Ok(())
+ }
+}
+
+struct Sip {
+ listeners: Vec<TcpListener>,
+}
+
+enum CharType {
+ Line,
+ Whitespace,
+ Other,
+}
+
+fn chartype(char: Option<&u8>) -> CharType {
+ match char {
+ None | Some(&LF) | Some(&CR) => CharType::Line,
+ Some(&SPACE) | Some(&TAB) => CharType::Whitespace,
+ Some(_) => CharType::Other,
+ }
+}
+
+impl Sip {
+ fn new() -> Sip {
+ Sip { listeners: Vec::new() }
+ }
+
+ pub fn add_listener(&mut self, listener: TcpListener) {
+ self.listeners.push(listener);
+ }
+
+ pub fn run(self, handle: Handle) -> Box<Future<Item = (), Error = std::io::Error>> {
+
+ let mut future: Box<Future<Item = (), Error = std::io::Error>> = Box::new(future::ok(()));
+
+ for listener in self.listeners {
+
+ let lis_handle = handle.clone();
+
+ // Iterate incoming connections
+ future = Box::new(future.join(listener.incoming().for_each(move |(tcp, _)| {
+
+ // Split up the read and write halves
+ let (_sink, stream) = tcp.framed(SipCodec::new()).split();
+
+ let future = stream.for_each(|message| {
+ println!("recevied sip message: {:?}", message);
+
+ Ok(())
+ })
+ .map_err(|err| println!("Error {:?}", err));
+
+ // Spawn the future as a concurrent task
+ lis_handle.spawn(future);
+
+ Ok(())
+ }))
+ .map(|_| ()));
+ }
+
+ future
+ }
+}
+
+#[cfg(test)]
+mod tests {
+
+ use codec::Sip;
+ use futures;
+ use futures::Future;
+ use futures::future::Either;
+ use tokio_io::io;
+ use tokio_core::net::TcpListener;
+ use tokio_core::net::TcpStream;
+ use tokio_core::reactor::Core;
+ use std::net::Shutdown;
+
+ #[test]
+ fn it_works() {
+ // Create the event loop that will drive this server
+ let mut core = Core::new().unwrap();
+ let handle = core.handle();
+
+ let mut sip = Sip::new();
+
+ // Bind the server's socket
+ let addr = "127.0.0.1:12345".parse().unwrap();
+ sip.add_listener(TcpListener::bind(&addr, &handle).unwrap());
+
+ let server = sip.run(core.handle());
+
+ // 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 finished = request.and_then(|(socket, _request)| {
+ futures::done(socket.shutdown(Shutdown::Write).map(|_| socket))
+ });
+
+ let response = finished.and_then(|socket| io::read_to_end(socket, Vec::new()));
+
+ // Spin up the server on the event loop, and connect the client to it
+ let res = core.run(server.select2(response));
+
+ // Response
+ match res {
+ Ok(Either::B(((_socket, response), _))) => {
+ // assert_eq!("echo", String::from_utf8_lossy(&response));
+ }
+ Ok(_) => panic!("Expected client to complete"),
+ Err(Either::A((err, _))) => panic!("{:?}", err),
+ Err(Either::B((err, _))) => panic!("{:?}", err),
+ }
+ }
+}
--- /dev/null
+use nom;
+use std;
+
+use nom::alpha as alphas;
+use nom::digit as digits;
+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()) }
+));
+
+fn is_mark(c: u8) -> bool {
+ c == b'-' || c == b'_' || c == b'.' || c == b'!' || c == b'~' || c == b'*' || c == b'\'' ||
+ c == b'(' || c == b')'
+}
+
+fn is_unreserved(c: u8) -> bool {
+ nom::is_alphanumeric(c) || is_mark(c)
+}
+
+fn is_reserved(c: u8) -> bool {
+ c == b';' || c == b'/' || c == b'?' || c == b':' || c == b'@' || c == b'&' || c == b'=' ||
+ c == b'+' || c == b'$' || c == b','
+}
+
+fn is_user_unreserved(c: u8) -> bool {
+ is_unreserved(c) || c == b'&' || c == b'=' || c == b'+' || c == b'$' || c == b',' ||
+ c == b';' || c == b'?' || c == b'/'
+}
+
+fn is_password_unreserved(c: u8) -> bool {
+ is_unreserved(c) || c == b'=' || c == b'+' || c == b'$' || c == b','
+}
+
+fn is_domain_label(c: u8) -> bool {
+ nom::is_alphanumeric(c) || c == b'-'
+}
+
+macro_rules! take_1 (
+ ($input:expr, $submac:ident!( $($args:tt)* )) => (
+ {
+ use nom::Slice;
+ let input = $input;
+
+ println!("Taking 1 from {:?}", input);
+
+ match input.iter().next() {
+ Some(c) => {
+ if $submac!(*c, $($args)*) {
+ let res:nom::IResult<&[u8],u8> = nom::IResult::Done(input.slice(1..), *c);
+ res
+ }
+ else
+ {
+ println!("Failed to take 1 from {:?} - {:?} doesn't match", input, c);
+
+ nom::IResult::Error(error_position!(nom::ErrorKind::TakeWhile1, input))
+ }
+ },
+ None => {
+ nom::IResult::Incomplete(nom::Needed::Size(1))
+ }
+ }
+ }
+ );
+ ($input:expr, $f:expr) => (
+ take_1!($input, call!($f));
+ );
+);
+
+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));
+named!(hex<&[u8], u8>, take_1!(nom::is_hex_digit));
+
+fn escaped(input: &[u8]) -> IResult<&[u8], u8> {
+ let result = preceded!(input, tag!(b"%"), pair!(hex, hex));
+
+ match result {
+ IResult::Done(left, (a, b)) => {
+ let value: u8 = ((a - (b'0' as u8)) << 4) + (b - b'0');
+ IResult::Done(left, value)
+ }
+ IResult::Incomplete(a) => IResult::Incomplete(a),
+ IResult::Error(a) => IResult::Error(a),
+ }
+}
+
+named!(unreserved<&[u8], u8>, take_1!(is_unreserved));
+named!(user_unreserved<&[u8], u8>, take_1!(is_user_unreserved));
+named!(password_unreserved<&[u8], u8>, take_1!(is_password_unreserved));
+
+// The BNF splits unreserved and user unreserved, but that's just a waste
+named!(user<&[u8], Vec<u8>>, many1!(alt!(
+ user_unreserved |
+ escaped)));
+
+named!(password<&[u8], Vec<u8>>, many1!(alt!(
+ password_unreserved |
+ escaped)));
+
+named!(userinfo<&[u8], (Vec<u8>, Option<Vec<u8>>)>, terminated!(
+ pair!(
+ user,
+ opt!(preceded!(tag!(b":"), password))),
+ tag!(b"@")));
+
+named!(domain_label<&[u8], Vec<u8>>, alt!(
+ alphanumerics => { |a : &[u8]| a.to_vec() } |
+ tuple!(
+ alphanumeric,
+ take_while1!(is_domain_label),
+ alphanumeric) => { |(a, b, c) : (u8, &[u8], u8)| {
+ let mut v = Vec::with_capacity(b.len() + 2);
+ v.push(a);
+ v.extend_from_slice(b);
+ v.push(c);
+ v
+ }}));
+
+named!(top_label<&[u8], Vec<u8>>, alt!(
+ alphas => { |a : &[u8]| a.to_vec() } |
+ tuple!(
+ alpha,
+ take_while1!(is_domain_label)) => { |(a, b) : (u8, &[u8])| {
+ let mut v = Vec::with_capacity(b.len() + 2);
+ v.push(a);
+ v.extend_from_slice(b);
+ v
+ }}));
+
+named!(hostname<&[u8], Hostname>, tuple!(
+ many0!(terminated!(domain_label, tag!(b"."))),
+ terminated!(top_label, opt!(tag!(b".")))));
+
+named!(ipv4_address<&[u8], (u8, u8, u8, u8)>, tuple!(
+ number,
+ preceded!(tag!(b"."), number),
+ preceded!(tag!(b"."), number),
+ preceded!(tag!(b"."), number)));
+
+fn is_ipv6_char(c: u8) -> bool {
+ nom::is_hex_digit(c) || c == b':'
+}
+
+named!(ipv6_address<&[u8], Vec<u8>>, delimited!(
+ tag!(b"["),
+ many1!(take_1!(is_ipv6_char)),
+ tag!(b"]")));
+
+named!(host<Host>, alt!(
+ hostname => { |h| Host::Hostname(h) } |
+ ipv4_address => { |(a, b, c, d)| Host::Ipv4Address(a,b,c,d) } |
+ ipv6_address => { |i| Host::Ipv6Address(i) }
+));
+
+named!(hostport<&[u8], HostPort>, tuple!(
+ host,
+ opt!(preceded!(tag!(":"), number))));
+
+named!(transport_param<Transport>, alt!(
+ tag!("tcp") => { |_| Transport::Tcp } |
+ tag!("udp") => { |_| Transport::Udp } |
+ tag!("sctp") => { |_| Transport::Sctp } |
+ tag!("tls") => { |_| Transport::Tls } |
+ token => { |token : &[u8]| Transport::Other(token.to_vec()) }
+));
+
+named!(user_param<UserParam>, alt!(
+ tag!("phone") => { |_| UserParam::Phone } |
+ tag!("ip") => { |_| UserParam::Ip } |
+ token => { |token : &[u8]| UserParam::Other(token.to_vec()) }));
+
+fn is_param_unreserved(c: u8) -> bool {
+ is_unreserved(c) || c == b'[' || c == b']' || c == b'/' || c == b':' || c == b'&' ||
+ c == b'+' || c == b'$'
+}
+
+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)
+ } |
+ preceded!(tag!("user="), user_param) => { |user| UriParameter::User(user) } |
+ preceded!(tag!("method="), method) => { |method| UriParameter::Method(method) } |
+ preceded!(tag!(b"ttl="), number) => { |ttl| UriParameter::Ttl(ttl) } |
+ preceded!(tag!("maddr="), recognize!(host)) => {
+ |host : &[u8]| UriParameter::Maddr(host.to_vec())
+ } |
+ tag!("lr") => { |ttl| UriParameter::Lr } |
+ pair!(param, opt!(preceded!(tag!(b"="), param))) => {
+ |(key, value)|
+ UriParameter::Other {
+ key: key,
+ value: value
+ }
+ }
+));
+
+named!(uri_parameters<&[u8], Vec<UriParameter>>, many0!(
+ preceded!(char!(';'), uri_parameter)));
+
+fn is_header_unreserved(c: u8) -> bool {
+ is_unreserved(c) || c == b'[' || c == b']' || c == b'/' || c == b'?' || c == b':' ||
+ c == b'+' || c == b'$'
+}
+
+named!(hname<&[u8], Vec<u8>>, many1!(alt!(
+ take_1!(is_header_unreserved) |
+ escaped)));
+
+named!(hvalue<&[u8], Vec<u8>>, many1!(alt!(
+ take_1!(is_header_unreserved) |
+ escaped)));
+
+named!(header<&[u8], Header>, separated_pair!(
+ hname,
+ tag!("="),
+ hvalue));
+
+named!(headers<&[u8], Headers>, preceded!(
+ char!('?'),
+ tuple!(
+ header,
+ many0!(
+ preceded!(char!('&'), header)))));
+
+named!(sip_uri<&[u8], (Option<UserInfo>, HostPort, Vec<UriParameter>, Option<Headers>)>, preceded!(
+ tag!("sip:"),
+ tuple!(
+ opt!(userinfo),
+ hostport,
+ uri_parameters,
+ opt!(headers)
+ )));
+
+named!(sips_uri<&[u8], (Option<UserInfo>, HostPort, Vec<UriParameter>, Option<Headers>)>, preceded!(
+ tag!("sips:"),
+ tuple!(
+ opt!(userinfo),
+ hostport,
+ uri_parameters,
+ opt!(headers)
+ )));
+
+fn is_scheme_unreserved(c: u8) -> bool {
+ nom::is_alphanumeric(c) || c == b'+' || c == b'-' || c == b'.'
+}
+
+named!(scheme<&[u8], Scheme>, tuple!(
+ alpha,
+ many0!(take_1!(is_scheme_unreserved))));
+
+fn is_path_char(c: u8) -> bool {
+ is_unreserved(c) || c == b':' || c == b'@' || c == b'&' || c == b'=' || c == b'+' ||
+ c == b'$' || c == b','
+}
+
+named!(path_param<&[u8], Vec<u8>>, many0!(alt!(
+ escaped |
+ take_1!(is_path_char))));
+
+named!(path_segment<&[u8], PathSegment>, pair!(
+ many0!(take_1!(is_path_char)),
+ opt!(preceded!(tag!(b";"), path_param))));
+
+named!(srvr<&[u8], (Option<UserInfo>, HostPort)>, tuple!(
+ opt!(userinfo),
+ hostport));
+
+fn is_reg_name_char(c: u8) -> bool {
+ is_unreserved(c) || c == b':' || c == b';' || c == b'$' || c == b'@' || c == b'&' ||
+ c == b'=' || c == b'+' || c == b','
+}
+
+named!(reg_name<&[u8], Vec<u8>>, many1!(alt!(
+ take_1!(is_reg_name_char) |
+ escaped)));
+
+named!(authority<&[u8], Authority>, alt!(
+ srvr => { |(u, h)| Authority::Server { userinfo: u, hostport: h } } |
+ reg_name => { |n| Authority::Name(n) } ));
+
+named!(abs_path<&[u8], AbsolutePath>, many1!(
+ preceded!(char!('/'), path_segment)));
+
+named!(net_path<&[u8], (Authority, Option<AbsolutePath>)>, preceded!(tag!("//"), pair!(
+ authority,
+ opt!(abs_path))));
+
+named!(query<&[u8], Vec<u8>>, many0!(uric));
+
+named!(hier_part<&[u8], (HierarchicalPath, Option<Vec<u8>>)>, tuple!(
+ alt!(
+ net_path => { |(a, p)| HierarchicalPath::Network{ authority: a, path: p } } |
+ abs_path => { |p| HierarchicalPath::Absolute(p) }
+ ),
+ opt!(preceded!(char!('?'), query))));
+
+fn is_uri_char_no_slash(c: u8) -> bool {
+ is_unreserved(c) || c == b';' || c == b'?' || c == b':' || c == b'@' || c == b'&' ||
+ c == b'=' || c == b'+' || c == b'$' || c == b','
+}
+
+named!(uric_no_slash<&[u8], u8>, alt!(
+ take_1!(is_uri_char_no_slash) |
+ escaped));
+
+fn is_uri_char(c: u8) -> bool {
+ is_unreserved(c) || is_reserved(c)
+}
+
+named!(uric<&[u8], u8>, alt!(
+ take_1!(is_uri_char) |
+ escaped));
+
+named!(opaque_part<&[u8], (u8, Vec<u8>)>, pair!(
+ uric_no_slash,
+ many0!(uric)));
+
+named!(absolute_uri<&[u8], (Scheme, UriPath)>, separated_pair!(
+ scheme,
+ char!(':'),
+ alt!(
+ hier_part => { |(h,q)| UriPath::Hierarchical { path: h, query: q } } |
+ opaque_part => { |(b, o)| UriPath::Opaque(b, o) }
+ )));
+
+named!(request_uri<&[u8], Uri>, alt!(
+ sip_uri => { |(u, hp, p, h)| Uri::Sip(SipUri {
+ user_info: u,
+ host_port: hp,
+ params: p,
+ headers: h }) } |
+ sips_uri => { |(u, hp, p, h)| Uri::Sips(SipUri {
+ user_info: u,
+ host_port: hp,
+ params: p,
+ headers: h }) } |
+ absolute_uri => { |(s, p)| Uri::Generic {
+ scheme: s,
+ path: p } }
+));
+
+named!(sip_version<&[u8], Version>, preceded!(
+ tag!("SIP/"),
+ dbg_dmp!(separated_pair!(
+ dbg_dmp!(number),
+ dbg_dmp!(tag!(b".")),
+ dbg_dmp!(number)))));
+
+named!(request_line<&[u8], (Method, Uri, Version)>, tuple!(
+ method,
+ delimited!(
+ tag!(" "),
+ request_uri,
+ tag!(" ")),
+ sip_version));
+
+fn is_reason_phrase_char(c: u8) -> bool {
+ is_reserved(c) || is_unreserved(c) || c == b' ' || c == b'\t'
+}
+
+named!(reason_phrase<Vec<u8>>, many0!(alt!(
+ escaped |
+ take_1!(is_reason_phrase_char))));
+
+named!(status_line<&[u8], (Version, u16, Vec<u8>)>, tuple!(
+ sip_version,
+ delimited!(
+ tag!(" "),
+ number,
+ tag!(" ")
+ ),
+ reason_phrase));
+
+named!(pub top_line<TopLine>, alt!(
+ request_line => { |(m,u,v)| TopLine::RequestLine(RequestLine {
+ method: m,
+ uri: u,
+ version: v
+ })} |
+ status_line => { |(v, c, r)| TopLine::StatusLine(StatusLine {
+ version: v,
+ code: c,
+ reason: r
+ })}));