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