http_mini_lib/utils/
http_request.rs

1use crate::traits::stream_trait::StreamTrait;
2use std::fmt::Display;
3use std::io::{BufRead, BufReader};
4use std::net::TcpStream;
5
6/// # Allowed request methods
7const REQUEST_METHODS: [&str; 6] = ["GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS"];
8
9/// # Http request parse errors
10#[derive(Debug, PartialEq)]
11pub enum ParseHttpRequestError {
12    BadLen,
13    NoMethod,
14    UnknownMethod,
15    NoPath,
16    NoProtocol,
17    UnknownProtocol,
18}
19
20impl Display for ParseHttpRequestError {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        write!(
23            f,
24            "{}",
25            match self {
26                ParseHttpRequestError::BadLen => "Invalid request format",
27                ParseHttpRequestError::NoMethod => "Missing request method",
28                ParseHttpRequestError::UnknownMethod => "Unknown request method",
29                ParseHttpRequestError::NoPath => "Missing request path",
30                ParseHttpRequestError::NoProtocol => "Missing request protocol",
31                ParseHttpRequestError::UnknownProtocol => "Unknown request protocol",
32            }
33        )
34    }
35}
36
37#[derive(Debug, PartialEq)]
38pub struct HttpRequest {
39    pub method: Option<String>,
40    pub protocol: Option<String>,
41    pub path: Option<String>,
42    pub headers: Vec<(String, String)>,
43    pub body: Option<String>,
44}
45
46impl StreamTrait for TcpStream {
47    /// # Stream parser
48    fn parse(&self) -> Result<HttpRequest, ParseHttpRequestError> {
49        let buf_reader = BufReader::new(self);
50        let mut lines_iterator = buf_reader.lines();
51
52        let mut request = HttpRequest {
53            method: None,
54            protocol: None,
55            path: None,
56            headers: vec![],
57            body: None,
58        };
59
60        let mut first_line_vector: Vec<&str>;
61        let mut has_first_line = false;
62        let mut request_line;
63        loop {
64            request_line = lines_iterator.next();
65            if request_line.is_none() {
66                break;
67            }
68
69            let line_content = request_line.unwrap().unwrap();
70            if line_content.is_empty() {
71                break;
72            }
73
74            if has_first_line {
75                // headers
76                let line_content_iterator = line_content.splitn(2, ": ").collect::<Vec<&str>>();
77                request.headers.push((
78                    line_content_iterator[0].to_string(),
79                    line_content_iterator[1].to_string(),
80                ));
81            } else {
82                first_line_vector = line_content.splitn(3, ' ').collect::<Vec<&str>>();
83                let init_result = init_request(&mut request, &first_line_vector);
84                if init_result.is_err() {
85                    return Err(init_result.err().unwrap());
86                }
87
88                has_first_line = true;
89            }
90        }
91
92        Ok(request)
93    }
94}
95
96/// # Process Http request
97fn init_request(
98    http_request: &mut HttpRequest,
99    first_line_vector: &[&str],
100) -> Result<(), ParseHttpRequestError> {
101    if first_line_vector.len() != 3 {
102        return Err(ParseHttpRequestError::BadLen);
103    }
104
105    // Method
106    if first_line_vector[0].is_empty() {
107        return Err(ParseHttpRequestError::NoMethod);
108    }
109    if !REQUEST_METHODS.contains(&first_line_vector[0]) {
110        return Err(ParseHttpRequestError::UnknownMethod);
111    }
112    http_request.method = Option::from(first_line_vector[0].to_string());
113
114    // Path
115    if !first_line_vector[1].is_empty() {
116        http_request.path = Option::from(first_line_vector[1].to_string());
117    } else {
118        return Err(ParseHttpRequestError::NoPath);
119    }
120
121    // Protocol
122    if first_line_vector[2].is_empty() {
123        return Err(ParseHttpRequestError::NoProtocol);
124    }
125    if !first_line_vector[2].contains("HTTP") {
126        return Err(ParseHttpRequestError::UnknownProtocol);
127    }
128    http_request.protocol = Option::from(first_line_vector[2].to_string());
129
130    Ok(())
131}