1use http::{Request, Response, StatusCode, Version};
5
6use std::io::{BufRead, Write};
7
8use crate::systems::RawResponse;
9
10#[derive(Debug, PartialEq)]
12pub enum ParseError {
13 MalformedRequest,
14 ReadError,
15
16 InvalidMethod,
17 InvalidProtocolVer,
18 InvalidRequestParts,
19}
20
21impl std::fmt::Display for ParseError {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 match self {
24 ParseError::MalformedRequest => write!(f, "Malformed Request"),
25 ParseError::ReadError => write!(f, "Read Error"),
26
27 ParseError::InvalidMethod => write!(f, "Invalid Method"),
28 ParseError::InvalidProtocolVer => write!(f, "Invalid Protocol"),
29 ParseError::InvalidRequestParts => write!(f, "Invalid Request Parts"),
30 }
31 }
32}
33
34impl std::error::Error for ParseError {}
35
36pub trait VersionExt: Sized {
37 fn parse_version(s: &str) -> Result<Self, ParseError>;
41
42 fn to_string(&self) -> String;
43}
44
45impl VersionExt for Version {
46 fn parse_version(s: &str) -> Result<Version, ParseError> {
47 Ok(match s {
48 "HTTP/0.9" => Version::HTTP_09,
49 "HTTP/1.0" => Version::HTTP_10,
50 "HTTP/1.1" => Version::HTTP_11,
51 "HTTP/2.0" => Version::HTTP_2,
52 "HTTP/3.0" => Version::HTTP_3,
53 _ => return Err(ParseError::InvalidProtocolVer),
54 })
55 }
56
57 fn to_string(&self) -> String {
58 match *self {
59 Version::HTTP_09 => "HTTP/0.9".to_string(),
60 Version::HTTP_10 => "HTTP/1.0".to_string(),
61 Version::HTTP_11 => "HTTP/1.1".to_string(),
62 Version::HTTP_2 => "HTTP/2.0".to_string(),
63 Version::HTTP_3 => "HTTP/3.0".to_string(),
64 _ => unreachable!(),
65 }
66 }
67}
68
69fn validate_method(method: &str) -> bool {
70 matches!(
71 method,
72 "GET" | "POST" | "PUT" | "DELETE" | "HEAD" | "OPTIONS" | "CONNECT" | "TRACE" | "PATH"
73 )
74}
75
76pub fn take_request<R>(reader: &mut R) -> Result<Request<()>, ParseError>
78where
79 R: BufRead,
80{
81 let mut lines = reader.lines();
82
83 let line = lines
84 .next()
85 .ok_or(ParseError::MalformedRequest)?
86 .map_err(|_| ParseError::ReadError)?;
87
88 let mut parts = line.split(' ');
89
90 let method = parts.next().ok_or(ParseError::MalformedRequest)?;
91
92 if !validate_method(method) {
93 return Err(ParseError::InvalidMethod);
94 }
95
96 let uri = parts.next().ok_or(ParseError::MalformedRequest)?;
97
98 let version = parts.next().ok_or(ParseError::MalformedRequest)?;
99
100 let mut req = Request::builder()
101 .method(method)
102 .uri(uri)
103 .version(Version::parse_version(version)?);
104
105 while let Some(line) = lines
106 .next()
107 .transpose()
108 .map_err(|_| ParseError::ReadError)?
109 {
110 if line.is_empty() {
111 break;
112 }
113
114 let h = line.split_once(": ").ok_or(ParseError::MalformedRequest)?;
115
116 if h.1.is_empty() {
117 return Err(ParseError::MalformedRequest);
118 }
119
120 req = req.header(h.0, h.1);
121 }
122
123 req.body(()).map_err(|_| ParseError::MalformedRequest)
124}
125
126fn parse_response_line_into_buf<T>(
127 buf: &mut Vec<u8>,
128 request: &Response<T>,
129) -> Result<(), std::io::Error> {
130 write!(
131 buf,
132 "{} {}\r\n",
133 request.version().to_string(),
134 request.status()
135 )?;
136
137 for (key, value) in request.headers() {
138 let _ = buf.write(key.as_str().as_bytes())?;
139
140 write!(buf, ": ")?;
141
142 let _ = buf.write(value.as_bytes())?;
143
144 write!(buf, "\r\n")?;
145 }
146
147 write!(buf, "\r\n")?;
148
149 Ok(())
150}
151
152impl<T> IntoRawBytes for Response<T>
153where
154 T: IntoRawBytes,
155{
156 fn into_raw_bytes(self) -> Vec<u8> {
157 let mut buf = vec![];
158
159 let _ = parse_response_line_into_buf(&mut buf, &self);
161
162 buf.extend_from_slice(self.map(IntoRawBytes::into_raw_bytes).body());
163
164 buf
165 }
166}
167
168pub trait IntoRawBytes {
169 fn into_raw_bytes(self) -> Vec<u8>;
170}
171
172impl IntoRawBytes for () {
173 fn into_raw_bytes(self) -> Vec<u8> {
174 vec![]
175 }
176}
177
178impl IntoRawBytes for Vec<u8> {
179 fn into_raw_bytes(self) -> Vec<u8> {
180 self
181 }
182}
183
184impl IntoRawBytes for String {
185 fn into_raw_bytes(self) -> Vec<u8> {
186 self.into_bytes()
187 }
188}
189
190pub trait ResponseExt: Sized {
191 fn base(code: StatusCode) -> Response<()>;
192
193 fn empty(code: impl Into<StatusCode>) -> Response<()>;
194
195 fn into_raw_response(self) -> RawResponse;
196}
197
198impl<T> ResponseExt for Response<T>
199where
200 T: IntoRawBytes,
201{
202 fn base(code: StatusCode) -> Response<()> {
203 Response::builder().status(code).body(()).unwrap()
204 }
205
206 fn empty(code: impl Into<StatusCode>) -> Response<()> {
207 Response::builder().status(code.into()).body(()).unwrap()
208 }
209
210 fn into_raw_response(self) -> RawResponse {
211 self.map(IntoRawBytes::into_raw_bytes)
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use std::io::BufReader;
218
219 use super::*;
220 use http::{HeaderValue, Method, Version};
221
222 #[test]
223 fn sanity_check() {
224 let bytes = b"POST /api/send-data HTTP/1.1 \r\nHost: example.com\r\nUser-Agent: My-HTTP-Client/1.0\r\nAccept: application/json\r\nContent-Type: application/json\r\nContent-Length: 87\r\nAuthorization: 82u27ydcfkjegh8jndnkzJJFFJRGHN\r\n\r\n{\"user_id\":12345, \"event_type\":\"page_view\",\"timestamp\":\"2023-09-23T15:30:00Z\",\"data\":{\"page_url\":\"https://example.com/some-page\",\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36\",\"referrer\":\"https://google.com/search?q=example\",\"browser_language\":\"en-US\",\"device_type\":\"desktop\"}}";
225 let mut reader = BufReader::new(&bytes[..]);
226
227 let req = take_request(&mut reader);
228 assert!(req.is_ok());
229
230 let req = req.unwrap();
231 assert_eq!(req.method(), Method::POST);
232 assert_eq!(req.uri(), "/api/send-data");
233 assert_eq!(req.version(), Version::HTTP_11);
234 assert_eq!(req.headers().len(), 6);
235 assert_eq!(
236 req.headers().get("Host"),
237 Some(&HeaderValue::from_str("example.com").unwrap())
238 );
239 assert_eq!(
240 req.headers().get("User-Agent"),
241 Some(&HeaderValue::from_str("My-HTTP-Client/1.0").unwrap())
242 );
243 assert_eq!(
244 req.headers().get("Accept"),
245 Some(&HeaderValue::from_str("application/json").unwrap())
246 );
247 assert_eq!(
248 req.headers().get("Content-Type"),
249 Some(&HeaderValue::from_str("application/json").unwrap())
250 );
251 assert_eq!(
252 req.headers().get("Content-Length"),
253 Some(&HeaderValue::from_str("87").unwrap())
254 );
255 }
256
257 #[test]
258 fn invalid_protocol() {
259 let bytes = b"GET / HTTP/1.32 \r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 123\r\n\r\n";
260 let mut reader = BufReader::new(&bytes[..]);
261
262 let resp = take_request(&mut reader);
263 assert!(matches!(resp, Err(ParseError::InvalidProtocolVer)));
264 }
265
266 #[test]
267 fn invalid_header() {
268 let bytes = b"GET / HTTP/1.1 22 3jklajs \r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 123\r\n\r\n";
269 let mut reader = BufReader::new(&bytes[..]);
270
271 let resp = take_request(&mut reader);
272
273 assert!(matches!(resp, Err(ParseError::MalformedRequest)));
274
275 let bytes = b"GET / jjshjudh HTTP/1.1 22 3jklajs \r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 123\r\n\r\n";
276
277 let mut reader = BufReader::new(&bytes[..]);
278
279 let resp = take_request(&mut reader);
280 assert!(matches!(resp, Err(ParseError::MalformedRequest)));
281 }
282
283 #[test]
284 fn invalid_method() {
285 let bytes = b"BAKLAVA / HTTP/1.1 \r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 123\r\n\r\n";
286
287 let mut reader = BufReader::new(&bytes[..]);
288
289 let resp = take_request(&mut reader);
290 assert!(matches!(resp, Err(ParseError::InvalidMethod)));
291 }
292
293 #[test]
294 fn not_enough_bytes() {
295 let bytes = b"GET / HT";
298 let mut reader = BufReader::new(&bytes[..]);
299
300 let resp = take_request(&mut reader);
301 assert!(matches!(resp, Err(ParseError::MalformedRequest)));
303
304 let bytes = b"GET / HTTP/1.1 \r\nContent-Type: \r\n\r\n";
314 let mut reader = BufReader::new(&bytes[..]);
315
316 let resp = take_request(&mut reader);
317 assert!(matches!(resp, Err(ParseError::MalformedRequest)));
319
320 let bytes = b"GET / HTTP/1.1 \r\nContent-Type: \r\nContent-L";
322 let mut reader = BufReader::new(&bytes[..]);
323
324 let resp = take_request(&mut reader);
325 assert!(matches!(resp, Err(ParseError::MalformedRequest)));
326 }
327
328 #[test]
329 fn unicode_in_request() {
330 let bytes = b"GET / HTTP/1.1 \r\nContent-Type: \xE2\xA1\x91\xE2\xB4\x9D\xE2\x9B\xB6\r\nContent-Length: 123\r\n\r\n";
331 let mut reader = BufReader::new(&bytes[..]);
332
333 let resp = take_request(&mut reader);
334 assert!(resp.is_ok());
335 }
336
337 #[test]
338 fn malformed_request() {
339 let bytes = b"GET / HTTP/1.1 \r\nContent-Type: \r\nContent-Length: 123\r\n\r\n";
341 let mut reader = BufReader::new(&bytes[..]);
342
343 let resp = take_request(&mut reader);
344 assert!(matches!(resp, Err(ParseError::MalformedRequest)));
345
346 let bytes = b"GET / HTTP/1.1\r\nCey: text/html; charset=utf-8\r\nContent-Len\r\n\r\n";
348 let mut reader = BufReader::new(&bytes[..]);
349
350 let resp = take_request(&mut reader);
351 assert!(matches!(resp, Err(ParseError::MalformedRequest)));
352
353 let bytes = b"GET / HTTP/1.1\r\nContent-Type: text/html; charset=utf-8\rContent-Length: 123\r\n\r\n";
354 let mut reader = BufReader::new(&bytes[..]);
355
356 let resp = take_request(&mut reader);
357 assert!(matches!(resp, Err(ParseError::MalformedRequest)));
358
359 let bytes = b"GET / HTTP/1.1\r\nContent-Type: text/html; charset=utf-8\r\n\n";
361 let mut reader = BufReader::new(&bytes[..]);
362
363 let resp = take_request(&mut reader);
364 assert!(matches!(resp, Err(ParseError::MalformedRequest)));
366
367 let bytes = b"GET / HTTP/1.1\r\nContent-Type: text/html; charset=utf-8\nContent-Length: 123\r\n\r\n";
368 let mut reader = BufReader::new(&bytes[..]);
369
370 let resp = take_request(&mut reader);
371 assert!(matches!(resp, Err(ParseError::MalformedRequest)));
373 }
374
375 #[test]
376 fn nulls_in_request() {
377 let bytes = b"GET / HTTP/1.1\r\nContent-Type: \0\r\nContent-Length: 123\r\n\r\n";
378 let mut reader = BufReader::new(&bytes[..]);
379
380 let resp = take_request(&mut reader);
381 assert!(matches!(resp, Err(_)));
382
383 let bytes =
384 b"GET / HTTP/1.1\r\n\0: text/html; charset=utf-8\r\nContent-Length: 123\r\n\r\n";
385 let mut reader = BufReader::new(&bytes[..]);
386
387 let resp = take_request(&mut reader);
388 assert!(matches!(resp, Err(_)));
389
390 let bytes = b"\0 \0 \0\r\n\0: text/html; charset=utf-8\r\nContent-Length: 123\r\n\r\n";
391 let mut reader = BufReader::new(&bytes[..]);
392
393 let resp = take_request(&mut reader);
394 assert!(matches!(resp, Err(_)));
395 }
396}