1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#[macro_use] extern crate nom;

use nom::{digit, alphanumeric};
use std::str;

// HTTP-name     = %x48.54.54.50 ; "HTTP", case-sensitive
named!(http_name, tag!("HTTP"));

#[derive(PartialEq, Debug)]
pub struct Version {
    major: u8,
    minor: u8,
}

#[macro_export]
macro_rules! filter (
  ($i:expr, $c: expr) => (
    {
      if $i.is_empty() {
        nom::IResult::Incomplete(nom::Needed::Size(1))
      } else {
        if $c($i[0]) {
          nom::IResult::Done(&$i[1..], &$i[0..1])
        } else {
          nom::IResult::Error(error_position!(nom::ErrorKind::Char, $i))
        }
      }
    }
  );
);

fn as_digit(slice: &[u8]) -> u8 {
    slice[0] - 48
}

// HTTP-version  = HTTP-name "/" DIGIT "." DIGIT
named!(http_version<Version>, do_parse!(
    http_name >> tag!("/") >> major: digit >> tag!(".") >> minor: digit >>
    (Version { major: as_digit(major), minor: as_digit(minor)})
  ));

named!(space, tag!(" "));
named!(crlf, tag!("\r\n"));

// TODO: Do it properly!
named!(request_target, is_not!(" "));

// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
//named!(tchar, alt!(one_of!("!#$%&'*+-.^_`|~") | alphanumeric));

////token = 1*tchar
//named!(token, many1!(tchar));

//method = token
//named!(method, call!(token));

//request-line   = method SP request-target SP HTTP-version CRLF
//named!(request_line, do_parse!(
//    m: token >> space >> uri: request_target >> space >> version: http_version >> crlf
//    (m, uri, version)
//  ));

/*
start-line     = request-line / status-line


 HTTP-message   = start-line
                      *( header-field CRLF )
                      CRLF
                      [ message-body ]
 */

#[cfg(test)]
mod tests {
    use super::*;
    use nom::IResult;

    #[test]
    fn http_name() {
        assert_eq!(super::http_name(&b"HTTP"[..]), IResult::Done(&b""[..], &b"HTTP"[..]));
    }

    #[test]
    fn http_version() {
        assert_eq!(super::http_version(&b"HTTP/1.2"[..]), IResult::Done(&b""[..], Version { major: 1, minor: 2 }));
    }

    #[test]
    fn request_line() {
//        assert_eq!(super::request_line(&b"GET /a/test HTTP/1.1\r\n"[..]), IResult::Done(&b""[..], (&b"GET"[..], &b"/a/test"[..], (&b"1"[..], &b"1"[..]))));
    }
}