1use nom::branch::alt;
2use nom::bytes::complete::{is_a, is_not, take, take_until1};
3use nom::bytes::streaming::{tag, tag_no_case, take_while};
4use nom::character::is_alphanumeric;
5use nom::IResult;
7
8pub fn crlf(i: &str) -> IResult<&str, &str> {
13 tag("\r\n")(i)
14}
15
16pub fn colon(i: &str) -> IResult<&str, &str> {
17 tag(":")(i)
18}
19
20pub fn space(i: &str) -> IResult<&str, &str> {
21 tag(" ")(i)
22}
23
24pub fn spaces(i: &str) -> IResult<&str, &str> {
28 is_a(" ")(i)
29}
30
31pub fn to_space(s: &str) -> IResult<&str, &str> {
36 is_not(" ")(s)
37}
38
39pub fn take_until_carriage_return(s: &str) -> IResult<&str, &str> {
40 take_until1("\r")(s)
41}
42
43pub fn is_digit_char(ch: char) -> bool {
47 let ch_u8 = ch as u8;
48 b"0123456789".contains(&ch_u8)
49}
50
51pub fn number(s: &str) -> IResult<&str, &str> {
52 take_while(is_digit_char)(s)
53}
54
55pub fn http_method(s: &str) -> IResult<&str, &str> {
63 alt((
64 tag("CONNECT"),
65 tag("DELETE"),
66 tag("GET"),
67 tag("HEAD"),
68 tag("OPTIONS"),
69 tag("PATCH"),
70 tag("POST"),
71 tag("PUT"),
72 tag("TRACE")
73 ))(s)
74}
75
76pub fn http_slash(s: &str) -> IResult<&str, &str> {
77 tag_no_case("HTTP/")(s)
78}
79pub fn http_version(s: &str) -> IResult<&str, &str> {
81 let result = http_slash(s);
82 match result {
83 Ok((s2, _)) => take(3usize)(s2),
84 Err(_) => result
85 }
86}
87
88fn is_http_header_name_char(ch: char) -> bool {
89 let ch_u8 = ch as u8;
90 is_alphanumeric(ch_u8) ||
91 b"!#$%&'*+-.^_`|~".contains(&ch_u8)
92}
93
94pub fn http_header_name(s: &str) -> IResult<&str, &str> {
95 take_while(is_http_header_name_char)(s)
96}
97
98#[cfg( feature = "tolerant-http1-parser")]
101pub fn is_header_value_char(ch: char) -> bool {
102 let ch_u8 = ch as u8;
103 ch_u8 == 9 || (ch_u8 >= 32 && ch_u8 <= 126) || i >= 160
104}
105
106#[cfg(not(feature = "tolerant-http1-parser"))]
107pub fn is_header_value_char(ch: char) -> bool {
108 let ch_u8 = ch as u8;
109 ch_u8 == 9 || (ch_u8 >= 32 && ch_u8 <= 126)
110}
111
112pub fn header_value(s: &str) -> IResult<&str, &str> {
113 take_while(is_header_value_char)(s)
114}
115
116
117#[cfg(test)]
121mod tests {
122 extern crate rand;
123
124 use super::*;
125 use nom::error::ErrorKind;
126 use nom::Err;
127 use nom::error::Error;
129
130
131 #[test]
132 fn test_crlf() {
133 assert_eq!(crlf("\r\n"), Ok(("", "\r\n")));
134 assert_eq!(crlf("\r\nWorld!"), Ok(("World!", "\r\n")));
135 assert_eq!(crlf("\r\nHello\r\nWorld!"), Ok(("Hello\r\nWorld!", "\r\n")));
136 assert_eq!(crlf("\r\n "), Ok((" ", "\r\n")));
137
138 let resp = crlf("Something");
139 let err = Err(Err::Error(Error::new("Something", ErrorKind::Tag)));
140 assert_eq!(resp, err);
141
142 let resp2 = crlf("Foo\r\nBar");
143 let err2 = Err(Err::Error(Error::new("Foo\r\nBar", ErrorKind::Tag)));
144 assert_eq!(resp2, err2);
145 }
146
147 #[test]
148 fn test_space() {
149 assert_eq!(space(" "), Ok(("", " ")));
150 assert_eq!(space(" Hello"), Ok(("Hello", " ")));
151 assert_eq!(space(" "), Ok((" ", " ")));
152
153 let resp = space("Rust World");
154 let err = Err(Err::Error(Error::new("Rust World", ErrorKind::Tag)));
155 assert_eq!(resp, err);
156 }
157
158 #[test]
159 fn test_spaces() {
160 assert_eq!(spaces(" "), Ok(("", " ")));
161 assert_eq!(spaces(" "), Ok(("", " ")));
162 assert_eq!(spaces(" cat"), Ok(("cat", " ")));
163 assert_eq!(spaces(" Hello"), Ok(("Hello", " ")));
164 assert_eq!(spaces(" "), Ok(("", " ")));
165 assert_eq!(spaces(" TREE "), Ok(("TREE ", " ")));
166
167 let resp = spaces("none");
168 let err = Err(Err::Error(Error::new("none", ErrorKind::IsA)));
169 assert_eq!(resp, err);
170 }
171
172 #[test]
184 fn test_http_method() {
185 assert_eq!(http_method("POST foo bar baz"), Ok((" foo bar baz", "POST")));
186 assert_eq!(http_method("HEAD foo bar baz"), Ok((" foo bar baz", "HEAD")));
187 assert_eq!(http_method("GET foo bar baz"), Ok((" foo bar baz", "GET")));
188 }
189}
190