| extern crate combine; | |
| use combine::*; | |
| use combine::primitives::Error; | |
| use combine::combinator::take_while1; | |
| use std::fs::File; | |
| use std::env; | |
| #[derive(Debug)] | |
| struct Request<'a> { | |
| method: &'a [u8], | |
| uri: &'a [u8], | |
| version: &'a [u8], | |
| } | |
| #[derive(Debug)] | |
| struct Header<'a> { | |
| name: &'a [u8], | |
| value: Vec<&'a [u8]>, | |
| } | |
| fn is_token(c: u8) -> bool { | |
| // roughly follows the order of ascii chars: "\"(),/:;<=>?@[\\]{} \t" | |
| c < 128 && c > 32 && c != b'\t' && c != b'"' && c != b'(' && c != b')' && | |
| c != b',' && c != b'/' && !(c > 57 && c < 65) && !(c > 90 && c < 94) && | |
| c != b'{' && c != b'}' | |
| } | |
| fn is_horizontal_space(c: u8) -> bool { c == b' ' || c == b'\t' } | |
| fn is_space(c: u8) -> bool { c == b' ' } | |
| fn is_not_space(c: u8) -> bool { c != b' ' } | |
| fn is_http_version(c: u8) -> bool { c >= b'0' && c <= b'9' || c == b'.' } | |
| fn main() { | |
| let mut contents: Vec<u8> = Vec::new(); | |
| { | |
| use std::io::Read; | |
| let mut file = File::open(env::args().nth(1).expect("File to read")).ok().expect("Failed to open file"); | |
| let _ = file.read_to_end(&mut contents).unwrap(); | |
| } | |
| // Making a closure, because parser instances cannot be reused | |
| let end_of_line = || (satisfy(|c| c == b'\r').skip(satisfy(|c| c == b'\n'))).or(satisfy(|c| c == b'\n')); | |
| // let mut http_version = string(&b"HTTP/"[..]) | |
| let mut http_version = satisfy(|c| c == b'H') | |
| .with(satisfy(|c| c == b'T')) | |
| .with(satisfy(|c| c == b'T')) | |
| .with(satisfy(|c| c == b'P')) | |
| .with(satisfy(|c| c == b'/')) | |
| .with(take_while1(is_http_version)); | |
| let request_line = parser(|input| ( | |
| // dereferencing pointers before checking if it is a token | |
| take_while1(is_token), | |
| skip_many1(satisfy(|c| is_space(c))), | |
| take_while1(is_not_space), | |
| skip_many1(satisfy(|c| is_space(c))), | |
| &mut http_version, | |
| ).map(|(method, _, uri, _, version)| Request { | |
| method: method, | |
| uri: uri, | |
| version: version, | |
| }) | |
| .parse_lazy(input) | |
| ); | |
| let message_header = parser(|input| { | |
| let message_header_line = ( | |
| skip_many1(satisfy(|c| is_horizontal_space(c))), | |
| take_while1(|c| c != b'\r' && c != b'\n'), | |
| end_of_line() | |
| ).map(|(_, line, _)| line); | |
| ( | |
| take_while1(is_token), | |
| satisfy(|c| c == b':'), | |
| many1(message_header_line) | |
| ).map(|(name, _, value)| Header { | |
| name: name, | |
| value: value, | |
| }) | |
| .parse_lazy(input) | |
| }); | |
| let mut request = ( | |
| request_line, | |
| end_of_line(), | |
| many(message_header), | |
| end_of_line() | |
| ).map(|(request, _, headers, _)| (request, headers)); | |
| let mut i = 0; | |
| let mut buf = &contents[..]; | |
| loop { | |
| // Needed for inferrence for many(message_header) | |
| let r: Result<((Request, Vec<Header>), _), _> = request.parse(buf); | |
| match r { | |
| Ok(((_, _), b)) => { | |
| i = i + 1; | |
| buf = b | |
| }, | |
| Err(e) => { | |
| if e.errors[0] == Error::end_of_input() { | |
| return | |
| } | |
| panic!("{:?}", e); | |
| } | |
| } | |
| if buf.is_empty() { | |
| break; | |
| } | |
| } | |
| println!("num: {}", i); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment