1use crate::{
2 http::{Header, Method, Version},
3 utils,
4};
5use bytes::Bytes;
6use core::fmt;
7use memchr::{memchr, memchr_iter, memmem};
8use std::{collections::HashMap, format, vec};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub enum FormTypes {
12 None,
13 MultiPart(HashMap<String, MultiPartFormEntry>),
14 XUrlEncoded(HashMap<String, String>), }
16
17#[derive(PartialEq, Debug, Clone)]
18pub struct Request {
19 method: Method,
20 path: String,
21 version: Version,
22 host: String,
23 query_string: Option<String>,
24 headers: HashMap<String, String>,
25 body: Vec<u8>,
26 form_data: FormTypes,
27 keep_alive: bool,
28}
29
30#[derive(PartialEq, Debug, Clone, Copy)]
31pub enum Error {
32 InvalidString,
33 InvalidMethod,
34 InvalidHTTPVersion,
35 MissingBlankLine,
36 NoHostHeader,
37 InvalidContentLength,
38 WaitingOnBody(Option<usize>), MissingMultiPartBoundary,
40 MissingContentLength,
41 InvalidUrlEncodedForm,
42}
43
44impl std::error::Error for Error {
45 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
46 None
47 }
48}
49
50impl From<&Error> for String {
51 fn from(value: &Error) -> Self {
52 match value {
53 Error::InvalidString => "Invalid String".to_string(),
54 Error::NoHostHeader => "No VHost Specified".to_string(),
55 Error::InvalidMethod => "Invalid Method Requested".to_string(),
56 Error::InvalidHTTPVersion => "Unsupported HTTP version Request".to_string(),
57 Error::MissingBlankLine => "Missing Blank Line".to_string(),
58 Error::WaitingOnBody(_) => "Waiting On Body".to_string(),
59 Error::InvalidContentLength => "Content Length Invalid".to_string(),
60 Error::MissingMultiPartBoundary => "Missing Mulipart boundary".to_string(),
61 Error::MissingContentLength => "Missing Content Length Header".to_string(),
62 Error::InvalidUrlEncodedForm => "Invalid URL Encoded Form".to_string(),
63 }
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq)]
68pub struct MultiPartFormEntry {
69 name: String,
70 file_name: Option<String>,
71 content_type: Option<String>,
72 value: Vec<u8>,
73}
74
75impl MultiPartFormEntry {
76 pub fn from_string(form_str: &str) -> Result<MultiPartFormEntry, anyhow::Error> {
77 let split: Vec<_> = form_str.split("\r\n\r\n").collect();
79 if let (Some(header), Some(body)) = (split.first(), split.get(1)) {
80 let lines = header.split("\r\n");
81 let mut form_args: HashMap<&str, &str> = HashMap::new();
82 let mut content_type = None;
83 for line in lines {
84 let name_value_split: Vec<_> = line.split(": ").collect();
85 if let (Some(header_name), Some(header_value)) =
86 (name_value_split.first(), name_value_split.get(1))
87 {
88 match header_name.to_lowercase().as_str() {
89 "content-type" => {
90 content_type = Some(header_value.to_string());
91 }
92 "content-disposition" => {
93 let split = header_value.split("; ");
94 for op in split {
95 let nv: Vec<_> = op.split('=').collect();
96 if let (Some(n), Some(v)) = (nv.first(), nv.get(1)) {
97 form_args.insert(n, strip_quotes(v));
98 }
99 }
100 }
101 _ => {}
102 }
103 }
104 }
105 if let Some(name) = form_args.get("name") {
106 let name = name.to_string();
107 let file = form_args.get("filename").map(|s| s.to_string());
108 Ok(MultiPartFormEntry {
109 name,
110 file_name: file,
111 content_type,
112 value: body.as_bytes().into(),
113 })
114 } else {
115 Err(anyhow::Error::msg("Missing Name"))
116 }
117 } else {
118 Err(anyhow::Error::msg("Missing Body"))
119 }
120 }
121
122 pub fn from_bytes(form: &[u8]) -> Result<MultiPartFormEntry, anyhow::Error> {
123 if let Some(blank_line) = memmem::find(form, b"\r\n\r\n") {
125 let mut form_args: HashMap<String, String> = HashMap::new();
126 let mut content_type = None;
127 let header = &form[0..blank_line + 2];
128 let mut body = &form[blank_line + 4..];
129 if body[body.len() - 1] == b'\n' && body[body.len() - 2] == b'\r' {
130 body = &body[0..body.len() - 2]; }
132 let newline_iter = memmem::find_iter(header, "\r\n");
133 let mut last_header_start = 0;
134 for i in newline_iter {
135 let new_header = &header[last_header_start..i];
136 last_header_start = i + 2;
137 if let Some(colon_i) = memmem::find(new_header, b": ") {
138 let name_b = &new_header[0..colon_i];
139 let name = String::from_utf8_lossy(name_b);
140 let value = &new_header[colon_i + 2..];
141 match name.to_lowercase().as_str() {
142 "content-type" => {
143 content_type = Some(String::from_utf8_lossy(value).to_string());
144 }
145 "content-disposition" => {
146 let header_value = String::from_utf8_lossy(value).to_string();
147 let split = header_value.split("; ");
148 for op in split {
149 let nv: Vec<_> = op.split('=').collect();
150 if let (Some(n), Some(v)) = (nv.first(), nv.get(1)) {
151 form_args.insert(n.to_string(), strip_quotes(v).to_string());
152 }
153 }
154 }
155 _ => {}
156 }
157 }
158 }
159 if let Some(name) = form_args.get("name") {
160 let name = name.to_string();
161 let file = form_args.get("filename").map(|s| s.to_string());
162 Ok(MultiPartFormEntry {
163 name,
164 file_name: file,
165 content_type,
166 value: body.into(),
167 })
168 } else {
169 Err(anyhow::Error::msg("Missing Name"))
170 }
171 } else {
172 Err(anyhow::Error::msg("Missing Body"))
173 }
174 }
175
176 pub fn name_value(name: &str, value: &str) -> Self {
177 MultiPartFormEntry {
178 name: name.to_string(),
179 file_name: None,
180 content_type: None,
181 value: value.to_string().into(),
182 }
183 }
184
185 pub fn file(name: &str, file_name: &str, value: &str) -> Self {
186 MultiPartFormEntry {
187 name: name.to_string(),
188 file_name: Some(file_name.to_string()),
189 content_type: None,
190 value: value.to_string().into(),
191 }
192 }
193
194 pub fn file_name(&self) -> Option<&String> {
195 self.file_name.as_ref()
196 }
197
198 pub fn content_type(&self) -> Option<&String> {
199 self.content_type.as_ref()
200 }
201
202 pub fn value(&self) -> &[u8] {
203 self.value.as_ref()
204 }
205
206 pub fn name(&self) -> &str {
207 self.name.as_ref()
208 }
209}
210impl fmt::Display for Error {
211 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212 write!(f, "{}", String::from(self))
213 }
214}
215
216impl From<Error> for String {
217 fn from(value: Error) -> Self {
218 String::from(&value)
219 }
220}
221
222fn get_boundary(content_type_value_str: &str) -> Result<&str, anyhow::Error> {
223 let parts: Vec<_> = content_type_value_str.split(';').collect();
224 if parts.len() > 1 {
225 let nv: Vec<_> = parts[1].split('=').collect();
226 if let Some(boundary) = nv.get(1) {
227 Ok(strip_quotes(boundary))
228 } else {
229 Err(anyhow::Error::msg("Invalid boundary"))
230 }
231 } else {
232 Err(anyhow::Error::msg("Boundary Missing from string"))
233 }
234}
235
236fn get_multiparts_entries_from_bytes(
237 body: &[u8],
238 boundary: &[u8],
239) -> anyhow::Result<HashMap<String, MultiPartFormEntry>> {
240 let mut end_marker = vec![b'-', b'-'];
241 end_marker.extend_from_slice(boundary);
242 let boundary_marker = end_marker.clone();
243 end_marker.extend_from_slice(b"--");
244 if memmem::find(body, &end_marker).is_some() {
245 let mut body_spliter = memmem::find_iter(body, &boundary_marker);
248 let mut entries = HashMap::new();
249 if let Some(mut last_bound) = body_spliter.next() {
250 last_bound += boundary_marker.len();
251 for bound in body_spliter {
252 let current_body = &body[last_bound..bound];
253 last_bound = bound + boundary_marker.len();
254 if let Ok(entry) = MultiPartFormEntry::from_bytes(current_body) {
256 entries.insert(entry.name.clone(), entry);
257 }
258 }
259 } else {
260 return Err(anyhow::Error::msg("Missing boundaries"));
261 }
262 Ok(entries)
263 } else {
264 Err(anyhow::Error::msg("Not Full Body"))
265 }
266}
267
268fn strip_quotes(value: &str) -> &str {
269 let split: Vec<_> = value.split('\"').collect();
270 if let Some(v) = split.get(1) {
271 v
272 } else {
273 split[0]
274 }
275}
276
277impl Request {
278 pub fn ok(&self) -> String {
279 match self.version {
280 Version::V0_9 => "200 OK\r\n".to_owned(),
281 Version::V1_0 => "HTTP/1.0 200 OK\r\n".to_owned(),
282 Version::V1_1 => "HTTP/1.1 200 OK\r\n".to_owned(),
283 Version::V2_0 => "HTTP/2 200 OK\r\n".to_owned(),
284 }
285 }
286
287 pub fn error(&self, code: u32, message: &str) -> String {
288 format!("{} {} {}\r\n", self.version, code, message)
289 }
290
291 pub fn method(&self) -> &Method {
292 &self.method
293 }
294
295 pub fn path(&self) -> &str {
296 &self.path
297 }
298
299 pub fn version(&self) -> Version {
300 self.version
301 }
302
303 pub fn hostname(&self) -> &str {
304 &self.host
305 }
306
307 pub fn body(&self) -> &Vec<u8> {
308 &self.body
309 }
310
311 pub fn get_header_value(&self, header_name: &str) -> Option<String> {
312 let lower = header_name.to_lowercase();
313 Self::header_value(&self.headers, &lower)
314 }
315
316 pub fn header_value(headers: &HashMap<String, String>, header_name: &str) -> Option<String> {
317 let lower = header_name.to_lowercase();
318 return headers.get(&lower).cloned();
319 }
320
321 pub fn keep_alive(&self) -> bool {
322 self.keep_alive
323 }
324
325 pub fn from_lines(lines: &Vec<&str>) -> Result<Request, Error> {
326 let mut headers = HashMap::new();
327 let host;
328 let mut query_string = None;
329 let body = vec![];
330 let form_data = FormTypes::None;
331
332 let request_seperated: Vec<&str> = lines[0].split(' ').collect(); if request_seperated.len() < 3 {
334 return Err(Error::InvalidString);
335 }
336
337 let method = match request_seperated[0] {
339 "GET" => Method::GET,
340 "POST" => Method::POST,
341 _ => return Err(Error::InvalidMethod),
342 };
343
344 let url = request_seperated[1].to_string();
346 let url_split: Vec<&str> = url.split('?').collect(); let path = url_split[0].to_string();
348 if url_split.len() > 1 {
349 query_string = Some(url_split[1].to_string());
350 }
351
352 let version = match request_seperated[2] {
354 "HTTP/1.0" => Version::V1_0,
355 "HTTP/1.1" => Version::V1_1,
356 "HTTP/2.2" => Version::V2_0,
357 _ => return Err(Error::InvalidHTTPVersion),
358 };
359
360 if lines.len() > 1 {
362 for line in lines.iter().skip(1) {
364 if let Ok(header) = Header::try_from(*line) {
365 headers.insert(header.key, header.value);
366 }
367 }
369 }
370
371 let op_host = Self::header_value(&headers, "Host");
372 if let Some(hostname) = op_host {
373 let hostname_only: Vec<&str> = hostname.split(':').collect();
375 host = hostname_only[0].to_string();
376 } else {
377 return Err(Error::NoHostHeader);
379 }
380 let keep_alive = Self::determine_keep_alive(version, headers.get("connection"));
382 Ok(Request {
383 method,
384 version,
385 path,
386 headers,
387 host,
388 query_string,
389 body,
390 form_data,
391 keep_alive,
392 })
393 }
394
395 pub fn from_bytes(request_bytes: Bytes) -> Result<Request, Error> {
396 let bytes = request_bytes;
397 if bytes.is_empty() {
398 return Err(Error::InvalidString);
399 }
400 if let Some(blank_line_index) = memmem::find(&bytes, b"\r\n\r\n") {
402 let req_header = bytes.slice(0..blank_line_index + 2); let mut req_body = bytes.slice(blank_line_index + 4..bytes.len());
405 let mut req_header_lines = memmem::find_iter(&req_header, "\r\n");
406 if let Some(i) = req_header_lines.next() {
407 let url;
408 let mut headers = HashMap::new();
409 let host;
410 let mut query_string = None;
411 let mut form_data = FormTypes::None;
412 let request_line = req_header.slice(0..i);
413 let mut header_start = i + 2;
414 let mut space_iter = memchr_iter(b' ', &request_line);
415 let method_end = space_iter.next().ok_or(Error::InvalidString)?;
416 let url_end = space_iter.next().ok_or(Error::InvalidString)?;
417 let method_b = request_line.slice(0..method_end);
418 let url_b = request_line.slice(method_end + 1..url_end);
419 let version_b = request_line.slice(url_end + 1..request_line.len());
420
421 let method: Method =
422 Method::try_from(method_b.as_ref()).map_err(|_| Error::InvalidMethod)?;
423 let version =
424 Version::try_from(version_b.as_ref()).map_err(|_| Error::InvalidHTTPVersion)?;
425 if let Some(qmark) = memchr(b'?', &url_b) {
427 let query = url_b.slice(qmark + 1..url_b.len());
428 let url_slice = url_b.slice(0..qmark);
429 query_string = Some(String::from_utf8_lossy(query.as_ref()).to_string());
430 url = String::from_utf8_lossy(url_slice.as_ref()).to_string();
431 } else {
432 url = String::from_utf8_lossy(url_b.as_ref()).to_string();
433 }
434
435 for line_end in req_header_lines {
437 let header_line = req_header.slice(header_start..line_end);
438 header_start = line_end + 2;
439 if let Ok(header) = Header::try_from(header_line.as_ref()) {
440 headers.insert(header.key, header.value);
441 }
442 }
443
444 let op_host = Self::header_value(&headers, "Host");
446 if let Some(hostname) = op_host {
447 let hostname_only: Vec<&str> = hostname.split(':').collect();
449 host = hostname_only[0].to_string();
450 } else {
451 return Err(Error::NoHostHeader);
453 }
454
455 if let Some(content_length) = Self::header_value(&headers, "Content-Length") {
457 if let Ok(len) = content_length.parse() {
458 if req_body.len() < len {
459 return Err(Error::WaitingOnBody(Some(len - req_body.len())));
460 }
461 } else {
462 return Err(Error::InvalidContentLength);
463 }
464 }
465 if let Some(content_type) = Self::header_value(&headers, "Content-Type") {
466 match content_type {
467 x if x.contains("multipart/form-data;") => {
468 match get_boundary(&x) {
469 Ok(boundary) => {
470 match get_multiparts_entries_from_bytes(
471 &req_body,
472 boundary.as_bytes(),
473 ) {
474 Ok(entries) => {
475 form_data = FormTypes::MultiPart(entries);
476 req_body.clear(); }
479 Err(_) => {
480 return Err(Error::WaitingOnBody(None));
481 }
482 }
483 }
484 Err(e) => {
485 log::debug!("Error Parsing Boundary: {}", e.to_string());
486 return Err(Error::MissingMultiPartBoundary);
487 }
488 }
489 }
490 x if x.contains("application/x-www-form-urlencoded") => {
491 match utils::parse_query_string(&req_body) {
493 Ok(form) => {
494 form_data = FormTypes::XUrlEncoded(form);
495 req_body.clear();
496 }
497 Err(e) => {
498 log::error!(
499 "Error Parsing URL Encoded Body: {}",
500 e.to_string()
501 );
502 return Err(Error::InvalidUrlEncodedForm);
503 }
504 }
505 }
506 _ => {}
507 }
508 }
509 let keep_alive = Self::determine_keep_alive(version, headers.get("connection"));
510 Ok(Request {
511 method,
512 version,
513 path: url,
514 headers,
515 host,
516 query_string,
517 body: req_body.to_vec(),
518 form_data,
519 keep_alive,
520 })
521 } else {
522 panic!("request parsing: Somehow missing CRLF even though CRLFCRLF was present");
524 }
525 } else {
526 Err(Error::MissingBlankLine)
527 }
528 }
529
530 pub fn from_string(request_str: String) -> Result<Request, Error> {
531 let bytes = Bytes::from(request_str);
532 Self::from_bytes(bytes)
533 }
534
535 pub fn query_string(&self) -> Option<&String> {
536 self.query_string.as_ref()
537 }
538
539 pub fn form_data(&self) -> &FormTypes {
540 &self.form_data
541 }
542
543 fn determine_keep_alive(version: Version, connection_header: Option<&String>) -> bool {
547 if let Some(conn) = connection_header {
548 conn.to_lowercase() == "keep-alive"
549 } else {
550 match version {
552 Version::V0_9 => false,
553 Version::V1_0 => false,
554 Version::V1_1 => true,
555 Version::V2_0 => true,
556 }
557 }
558 }
559}
560
561#[cfg(test)]
562mod tests {
563 use std::assert_eq;
564
565 use super::*;
566
567 #[test]
568 fn x_url_encoded_form() {
569 let mut map = HashMap::new();
570 map.insert("field1".to_string(), "value1".to_string());
571 map.insert("field2".to_string(), "value2".to_string());
572
573 let expected = Request {
574 method: Method::POST,
575 version: Version::V1_1,
576 path: "/test".to_string(),
577 body: vec![],
578 headers: HashMap::from([
579 ("host".to_string(), "foo.example".to_string()),
580 (
581 "content-type".to_string(),
582 "application/x-www-form-urlencoded".to_string(),
583 ),
584 ("content-length".to_string(), "27".to_string()),
585 ]),
586 host: "foo.example".to_string(),
587 query_string: None,
588 form_data: FormTypes::XUrlEncoded(map),
589 keep_alive: true,
590 };
591 let request_str = Bytes::from_static(
592 b"POST /test HTTP/1.1\r\n\
593 Host: foo.example\r\n\
594 Content-Type: application/x-www-form-urlencoded\r\n\
595 Content-Length: 27\r\n\r\n\
596 field1=value1&field2=value2",
597 ); let request = Request::from_bytes(request_str).expect("Could not build request");
599 assert_eq!(expected, request);
600 }
601
602 #[test]
603 fn multipart_form() {
604 let field1 = MultiPartFormEntry::name_value("field1", "value1");
605 let field2 = MultiPartFormEntry::file("field2", "example.txt", "value2");
606 let mut map = HashMap::new();
607 map.insert(field1.name.clone(), field1);
608 map.insert(field2.name.clone(), field2);
609 let expected = Request {
610 method: Method::POST,
611 version: Version::V1_1,
612 path: "/test".to_string(),
613 body: vec![],
614 headers: HashMap::from([
615 ("host".to_string(), "foo.example".to_string()),
616 (
617 "content-type".to_string(),
618 "multipart/form-data;boundary=\"boundary\"".to_string(),
619 ),
620 ]),
621 host: "foo.example".to_string(),
622 query_string: None,
623 form_data: FormTypes::MultiPart(map),
624 keep_alive: true,
625 };
626 let request_str = Bytes::from_static(
627 b"POST /test HTTP/1.1\r\n\
628 Host: foo.example\r\n\
629 Content-Type: multipart/form-data;boundary=\"boundary\"\r\n\
630 \r\n\
631 --boundary\r\n\
632 Content-Disposition: form-data; name=\"field1\"\r\n\
633 \r\n\
634 value1\r\n\
635 --boundary\r\n\
636 Content-Disposition: form-data; name=\"field2\"; filename=\"example.txt\"\r\n\
637 \r\n\
638 value2\r\n\
639 --boundary--",
640 );
641
642 let request = Request::from_bytes(request_str).expect("Could not build request");
643 assert_eq!(expected, request);
644 }
645
646 #[test]
647 fn get_wrong_version_new() {
648 let expected = Err(Error::InvalidHTTPVersion);
649 let request =
650 Request::from_bytes(Bytes::from_static(b"GET / HTTP1.1\r\nHost: test\r\n\r\n"));
651 assert_eq!(expected, request);
652 }
653
654 #[test]
655 fn no_blank_line_new() {
656 let expected = Err(Error::MissingBlankLine);
657 let req_str = Bytes::from_static(b"GET / HTTP/1.1");
658 let request = Request::from_bytes(req_str);
659 assert_eq!(expected, request);
660 }
661
662 #[test]
663 fn new() {
664 let expected = Request {
665 method: Method::GET,
666 version: Version::V1_1,
667 path: "/".to_string(),
668 body: vec![],
669 headers: HashMap::from([("host".to_string(), "test".to_string())]),
670 host: "test".to_string(),
671 query_string: None,
672 form_data: FormTypes::None,
673 keep_alive: true,
674 };
675 let request =
676 Request::from_bytes(Bytes::from_static(b"GET / HTTP/1.1\r\nHost: test\r\n\r\n"))
677 .expect("Error Parsing");
678 assert_eq!(expected, request);
679 }
680
681 #[test]
682 fn new_query_string() {
683 let expected = Request {
684 method: Method::GET,
685 version: Version::V1_1,
686 path: "/index.html".to_string(),
687 body: vec![],
688 headers: HashMap::from([("host".to_string(), "test".to_string())]),
689 host: "test".to_string(),
690 query_string: Some("test=true".to_string()),
691 form_data: FormTypes::None,
692 keep_alive: true,
693 };
694 let request = Request::from_bytes(Bytes::from_static(
695 b"GET /index.html?test=true HTTP/1.1\r\nHost: test\r\n\r\n",
696 ))
697 .expect("Error Parsing");
698 assert_eq!(expected, request);
699 }
700
701 #[test]
702 fn new_headers() {
703 let expected = Request {
704 method: Method::GET,
705 version: Version::V1_1,
706 path: "/".to_string(),
707 body: vec![],
708 headers: HashMap::from([
709 ("host".to_string(), "test".to_string()),
710 ("header1".to_string(), "hi".to_string()),
711 ("header2".to_string(), "Bye".to_string()),
712 ]),
713 host: "test".to_string(),
714 query_string: None,
715 form_data: FormTypes::None,
716 keep_alive: true,
717 };
718 let request = Request::from_string(
719 "GET / HTTP/1.1\r\nhost: test\r\nheader1: hi\r\nheader2: Bye\r\n\r\n".to_owned(),
720 )
721 .expect("Error Parsing");
722 assert_eq!(expected, request);
723 }
724
725 #[test]
726 fn empty_string() {
727 let request = Request::from_string("".to_owned());
728 if let Err(error) = request {
729 assert!(error == Error::InvalidString);
730 } else {
731 panic!("No error");
732 }
733 }
734}