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