1 use winnow::combinator::seq;
2 use winnow::{
3 ascii::line_ending, combinator::repeat, prelude::*, stream::Partial, token::take_while,
4 };
5
6 pub type Stream<'i> = Partial<&'i [u8]>;
7
8 #[rustfmt::skip]
9 #[derive(Debug)]
10 #[allow(dead_code)]
11 pub struct Request<'a> {
12 method: &'a [u8],
13 uri: &'a [u8],
14 version: &'a [u8],
15 }
16
17 #[derive(Debug)]
18 #[allow(dead_code)]
19 pub struct Header<'a> {
20 name: &'a [u8],
21 value: Vec<&'a [u8]>,
22 }
23
parse(data: &[u8]) -> Option<Vec<(Request<'_>, Vec<Header<'_>>)>>24 pub fn parse(data: &[u8]) -> Option<Vec<(Request<'_>, Vec<Header<'_>>)>> {
25 let mut buf = Partial::new(data);
26 let mut v = Vec::new();
27 loop {
28 match request(&mut buf) {
29 Ok(r) => {
30 v.push(r);
31
32 if buf.is_empty() {
33 //println!("{}", i);
34 break;
35 }
36 }
37 Err(e) => {
38 println!("error: {:?}", e);
39 return None;
40 }
41 }
42 }
43
44 Some(v)
45 }
46
request<'s>(input: &mut Stream<'s>) -> PResult<(Request<'s>, Vec<Header<'s>>)>47 fn request<'s>(input: &mut Stream<'s>) -> PResult<(Request<'s>, Vec<Header<'s>>)> {
48 let req = request_line(input)?;
49 let h = repeat(1.., message_header).parse_next(input)?;
50 let _ = line_ending.parse_next(input)?;
51
52 Ok((req, h))
53 }
54
request_line<'s>(input: &mut Stream<'s>) -> PResult<Request<'s>>55 fn request_line<'s>(input: &mut Stream<'s>) -> PResult<Request<'s>> {
56 seq!( Request {
57 method: take_while(1.., is_token),
58 _: take_while(1.., is_space),
59 uri: take_while(1.., is_not_space),
60 _: take_while(1.., is_space),
61 version: http_version,
62 _: line_ending,
63 })
64 .parse_next(input)
65 }
66
http_version<'s>(input: &mut Stream<'s>) -> PResult<&'s [u8]>67 fn http_version<'s>(input: &mut Stream<'s>) -> PResult<&'s [u8]> {
68 let _ = "HTTP/".parse_next(input)?;
69 let version = take_while(1.., is_version).parse_next(input)?;
70
71 Ok(version)
72 }
73
message_header_value<'s>(input: &mut Stream<'s>) -> PResult<&'s [u8]>74 fn message_header_value<'s>(input: &mut Stream<'s>) -> PResult<&'s [u8]> {
75 let _ = take_while(1.., is_horizontal_space).parse_next(input)?;
76 let data = take_while(1.., till_line_ending).parse_next(input)?;
77 let _ = line_ending.parse_next(input)?;
78
79 Ok(data)
80 }
81
message_header<'s>(input: &mut Stream<'s>) -> PResult<Header<'s>>82 fn message_header<'s>(input: &mut Stream<'s>) -> PResult<Header<'s>> {
83 seq!(Header {
84 name: take_while(1.., is_token),
85 _: ':',
86 value: repeat(1.., message_header_value),
87 })
88 .parse_next(input)
89 }
90
91 #[rustfmt::skip]
92 #[allow(clippy::match_same_arms)]
93 #[allow(clippy::match_like_matches_macro)]
is_token(c: u8) -> bool94 fn is_token(c: u8) -> bool {
95 match c {
96 128..=255 => false,
97 0..=31 => false,
98 b'(' => false,
99 b')' => false,
100 b'<' => false,
101 b'>' => false,
102 b'@' => false,
103 b',' => false,
104 b';' => false,
105 b':' => false,
106 b'\\' => false,
107 b'"' => false,
108 b'/' => false,
109 b'[' => false,
110 b']' => false,
111 b'?' => false,
112 b'=' => false,
113 b'{' => false,
114 b'}' => false,
115 b' ' => false,
116 _ => true,
117 }
118 }
119
is_version(c: u8) -> bool120 fn is_version(c: u8) -> bool {
121 c.is_ascii_digit() || c == b'.'
122 }
123
till_line_ending(c: u8) -> bool124 fn till_line_ending(c: u8) -> bool {
125 c != b'\r' && c != b'\n'
126 }
127
is_space(c: u8) -> bool128 fn is_space(c: u8) -> bool {
129 c == b' '
130 }
131
is_not_space(c: u8) -> bool132 fn is_not_space(c: u8) -> bool {
133 c != b' '
134 }
135
is_horizontal_space(c: u8) -> bool136 fn is_horizontal_space(c: u8) -> bool {
137 c == b' ' || c == b'\t'
138 }
139