use nom::branch::alt;
use nom::bytes::complete::{is_a, is_not, take, take_until1};
use nom::bytes::streaming::{tag, tag_no_case, take_while};
use nom::character::is_alphanumeric;
use nom::IResult;
pub fn crlf(i: &str) -> IResult<&str, &str> {
tag("\r\n")(i)
}
pub fn colon(i: &str) -> IResult<&str, &str> {
tag(":")(i)
}
pub fn space(i: &str) -> IResult<&str, &str> {
tag(" ")(i)
}
pub fn spaces(i: &str) -> IResult<&str, &str> {
is_a(" ")(i)
}
pub fn to_space(s: &str) -> IResult<&str, &str> {
is_not(" ")(s)
}
pub fn take_until_carriage_return(s: &str) -> IResult<&str, &str> {
take_until1("\r")(s)
}
pub fn is_digit_char(ch: char) -> bool {
let ch_u8 = ch as u8;
b"0123456789".contains(&ch_u8)
}
pub fn number(s: &str) -> IResult<&str, &str> {
take_while(is_digit_char)(s)
}
pub fn http_method(s: &str) -> IResult<&str, &str> {
alt((
tag("CONNECT"),
tag("DELETE"),
tag("GET"),
tag("HEAD"),
tag("OPTIONS"),
tag("PATCH"),
tag("POST"),
tag("PUT"),
tag("TRACE")
))(s)
}
pub fn http_slash(s: &str) -> IResult<&str, &str> {
tag_no_case("HTTP/")(s)
}
pub fn http_version(s: &str) -> IResult<&str, &str> {
let result = http_slash(s);
match result {
Ok((s2, _)) => take(3usize)(s2),
Err(_) => result
}
}
fn is_http_header_name_char(ch: char) -> bool {
let ch_u8 = ch as u8;
is_alphanumeric(ch_u8) ||
b"!#$%&'*+-.^_`|~".contains(&ch_u8)
}
pub fn http_header_name(s: &str) -> IResult<&str, &str> {
take_while(is_http_header_name_char)(s)
}
#[cfg( feature = "tolerant-http1-parser")]
pub fn is_header_value_char(ch: char) -> bool {
let ch_u8 = ch as u8;
ch_u8 == 9 || (ch_u8 >= 32 && ch_u8 <= 126) || i >= 160
}
#[cfg(not(feature = "tolerant-http1-parser"))]
pub fn is_header_value_char(ch: char) -> bool {
let ch_u8 = ch as u8;
ch_u8 == 9 || (ch_u8 >= 32 && ch_u8 <= 126)
}
pub fn header_value(s: &str) -> IResult<&str, &str> {
take_while(is_header_value_char)(s)
}
#[cfg(test)]
mod tests {
extern crate rand;
use super::*;
use nom::error::ErrorKind;
use nom::Err;
use nom::error::Error;
#[test]
fn test_crlf() {
assert_eq!(crlf("\r\n"), Ok(("", "\r\n")));
assert_eq!(crlf("\r\nWorld!"), Ok(("World!", "\r\n")));
assert_eq!(crlf("\r\nHello\r\nWorld!"), Ok(("Hello\r\nWorld!", "\r\n")));
assert_eq!(crlf("\r\n "), Ok((" ", "\r\n")));
let resp = crlf("Something");
let err = Err(Err::Error(Error::new("Something", ErrorKind::Tag)));
assert_eq!(resp, err);
let resp2 = crlf("Foo\r\nBar");
let err2 = Err(Err::Error(Error::new("Foo\r\nBar", ErrorKind::Tag)));
assert_eq!(resp2, err2);
}
#[test]
fn test_space() {
assert_eq!(space(" "), Ok(("", " ")));
assert_eq!(space(" Hello"), Ok(("Hello", " ")));
assert_eq!(space(" "), Ok((" ", " ")));
let resp = space("Rust World");
let err = Err(Err::Error(Error::new("Rust World", ErrorKind::Tag)));
assert_eq!(resp, err);
}
#[test]
fn test_spaces() {
assert_eq!(spaces(" "), Ok(("", " ")));
assert_eq!(spaces(" "), Ok(("", " ")));
assert_eq!(spaces(" cat"), Ok(("cat", " ")));
assert_eq!(spaces(" Hello"), Ok(("Hello", " ")));
assert_eq!(spaces(" "), Ok(("", " ")));
assert_eq!(spaces(" TREE "), Ok(("TREE ", " ")));
let resp = spaces("none");
let err = Err(Err::Error(Error::new("none", ErrorKind::IsA)));
assert_eq!(resp, err);
}
#[test]
fn test_http_method() {
assert_eq!(http_method("POST foo bar baz"), Ok((" foo bar baz", "POST")));
assert_eq!(http_method("HEAD foo bar baz"), Ok((" foo bar baz", "HEAD")));
assert_eq!(http_method("GET foo bar baz"), Ok((" foo bar baz", "GET")));
}
}