rust_web_server/request/
mod.rs

1#[cfg(test)]
2mod tests;
3#[cfg(test)]
4mod example;
5
6use std::collections::HashMap;
7use std::io;
8use std::io::{BufRead, Cursor, Read};
9use crate::header::Header;
10use crate::ext::string_ext::StringExt;
11use crate::http::HTTP;
12use crate::symbol::SYMBOL;
13use url_build_parse::parse_url;
14use crate::url::URL;
15
16#[derive(PartialEq, Eq, Clone, Debug)]
17pub struct Request {
18    pub method: String,
19    pub request_uri: String,
20    pub http_version: String,
21    pub headers: Vec<Header>,
22    pub body: Vec<u8>,
23}
24
25#[derive(PartialEq, Eq, Clone, Debug)]
26pub struct Method {
27    pub get: &'static str,
28    pub head: &'static str,
29    pub post: &'static str,
30    pub put: &'static str,
31    pub delete: &'static str,
32    pub connect: &'static str,
33    pub options: &'static str,
34    pub trace: &'static str,
35    pub patch: &'static str,
36}
37
38pub const METHOD: Method = Method {
39    get: "GET",
40    head: "HEAD",
41    post: "POST",
42    put: "PUT",
43    delete: "DELETE",
44    connect: "CONNECT",
45    options: "OPTIONS",
46    trace: "TRACE",
47    patch: "PATCH",
48};
49
50impl Request {
51    pub const _ERROR_UNABLE_TO_PARSE_METHOD_AND_REQUEST_URI_AND_HTTP_VERSION: &'static str = "Unable to parse method, request uri and http version";
52
53    pub fn get_header(&self, name: String) -> Option<&Header> {
54        let header =  self.headers.iter().find(|x| x.name.to_lowercase() == name.to_lowercase());
55        header
56    }
57
58    pub fn get_domain(&self) -> Result<Option<String>, String> {
59        let boxed_host = &self.get_header(Header::_HOST.to_string());
60        if boxed_host.is_none() {
61            return Ok(None);
62        }
63
64        let host_header = boxed_host.unwrap();
65        let host = host_header.value.to_string();
66
67        let boxed_host_port = host.split_once(&SYMBOL.colon.to_string());
68        if boxed_host_port.is_none() {
69            return Ok(Some(host))
70        }
71
72        let (domain, _port_str) = boxed_host_port.unwrap();
73
74        Ok(Some(domain.to_string()))
75    }
76
77    pub fn get_port(&self) -> Result<Option<i128>, String> {
78        let boxed_host = &self.get_header(Header::_HOST.to_string());
79        if boxed_host.is_none() {
80            return Ok(None);
81        }
82
83        let host_header = boxed_host.unwrap();
84        let host = host_header.value.to_string();
85
86        let boxed_host_port = host.split_once(&SYMBOL.colon.to_string());
87        if boxed_host_port.is_none() {
88            return Ok(None)
89        }
90
91        let (_domain, port_str) = boxed_host_port.unwrap();
92
93        let boxed_port_i128 = port_str.parse::<i128>();
94        if boxed_port_i128.is_err() {
95            let message = boxed_port_i128.err().unwrap().to_string();
96            return Err(message);
97        }
98
99        let port = boxed_port_i128.unwrap();
100
101        Ok(Some(port))
102    }
103
104    pub fn get_query(&self) -> Result<Option<HashMap<String, String>>, String> {
105        self.get_uri_query()
106    }
107
108    pub fn get_uri_query(&self) -> Result<Option<HashMap<String, String>>, String> {
109        // it will return an error if unable to parse url
110        // it will return None if there are no query params
111        // scheme and host required for the parse_url function
112        let url_array = ["http://", "localhost/", &self.request_uri];
113        let url = url_array.join(SYMBOL.empty_string);
114
115        let boxed_url_components = URL::parse(&url);
116        if boxed_url_components.is_err() {
117            let message = boxed_url_components.err().unwrap().to_string();
118            return Err(message)
119        }
120        Ok(boxed_url_components.unwrap().query)
121    }
122
123    pub fn get_path(&self) -> Result<String, String> {
124        self.get_uri_path()
125    }
126
127    pub fn get_uri_path(&self) -> Result<String, String> {
128        // scheme and host required for the parse_url function
129        let url_array = ["http://", "localhost", &self.request_uri];
130        let url = url_array.join(SYMBOL.empty_string);
131
132        let boxed_url_components = parse_url(&url);
133        if boxed_url_components.is_err() {
134            let message = boxed_url_components.err().unwrap().to_string();
135            return Err(message)
136        }
137        Ok(boxed_url_components.unwrap().path)
138    }
139
140    pub fn method_list() -> Vec<String> {
141        let method_get = METHOD.get.to_string();
142        let method_head = METHOD.head.to_string();
143        let method_post = METHOD.post.to_string();
144        let method_put = METHOD.put.to_string();
145        let method_delete = METHOD.delete.to_string();
146        let method_connect = METHOD.connect.to_string();
147        let method_options = METHOD.options.to_string();
148        let method_trace = METHOD.trace.to_string();
149        let method_patch = METHOD.patch.to_string();
150
151        let method_list = vec![
152            method_get,
153            method_head,
154            method_post,
155            method_put,
156            method_delete,
157            method_connect,
158            method_options,
159            method_trace,
160            method_patch,
161        ];
162
163        method_list
164    }
165
166    // instance method way to generate a request
167    pub fn generate(&self) -> Vec<u8> {
168        let clone = self.clone();
169        let mut request = Request::_generate_request(clone).as_bytes().to_vec();
170
171        let mut body = self.body.clone();
172        request.append(&mut body);
173
174        request
175    }
176
177    // same as _generate_request, not renamed original for backward compatability
178    pub fn generate_request(request: Request) -> String {
179        Request::_generate_request(request)
180    }
181
182    pub fn _generate_request(request: Request) -> String {
183        let status = [
184            request.method,
185            request.request_uri,
186            request.http_version,
187            SYMBOL.new_line_carriage_return.to_string()
188        ].join(SYMBOL.whitespace);
189
190        let mut headers = SYMBOL.empty_string.to_string();
191        for header in request.headers {
192            let mut header_string = SYMBOL.empty_string.to_string();
193            header_string.push_str(&header.name);
194            header_string.push_str(Header::NAME_VALUE_SEPARATOR);
195            header_string.push_str(&header.value);
196            header_string.push_str(SYMBOL.new_line_carriage_return);
197            headers.push_str(&header_string);
198        }
199
200        let request = format!(
201            "{}{}{}",
202            status,
203            headers,
204            SYMBOL.new_line_carriage_return
205        );
206
207
208        request
209    }
210
211    pub fn parse(request_vec_u8: &[u8]) ->  Result<Request, String> {
212        Request::parse_request(request_vec_u8)
213    }
214
215    pub fn parse_request(request_vec_u8: &[u8]) ->  Result<Request, String> {
216        let mut cursor = io::Cursor::new(request_vec_u8);
217
218        let mut request = Request {
219            method: "".to_string(),
220            request_uri: "".to_string(),
221            http_version: "".to_string(),
222            headers: vec![],
223            body: vec![],
224        };
225
226        let content_length: usize = 0;
227        let iteration_number : usize = 0;
228        return match Request::cursor_read(&mut cursor, iteration_number, &mut request, content_length) {
229            Ok(_) => {
230                Ok(request)
231            }
232            Err(error_message) => {
233                Err(error_message)
234            }
235        }
236
237    }
238
239
240    pub fn parse_method_and_request_uri_and_http_version_string(http_version_status_code_reason_phrase: &str) -> Result<(String, String, String), String> {
241        let lowercase_unparsed_method_and_request_uri_and_http_version = http_version_status_code_reason_phrase.trim();
242
243        let boxed_split_without_method = lowercase_unparsed_method_and_request_uri_and_http_version.split_once(SYMBOL.whitespace);
244        if boxed_split_without_method.is_none() {
245            return Err(Request::_ERROR_UNABLE_TO_PARSE_METHOD_AND_REQUEST_URI_AND_HTTP_VERSION.to_string())
246        }
247
248        let (method, without_method) = boxed_split_without_method.unwrap();
249        let supported_methods = Request::method_list();
250        if !supported_methods.contains(&method.to_uppercase().to_string()) {
251            return Err(Request::_ERROR_UNABLE_TO_PARSE_METHOD_AND_REQUEST_URI_AND_HTTP_VERSION.to_string())
252        }
253
254        let boxed_without_method = without_method.split_once(SYMBOL.whitespace);
255        if boxed_without_method.is_none() {
256            return Err(Request::_ERROR_UNABLE_TO_PARSE_METHOD_AND_REQUEST_URI_AND_HTTP_VERSION.to_string())
257        }
258
259        let (request_uri, http_version) = boxed_without_method.unwrap();
260
261
262        let supported_http_versions = HTTP::version_list();
263        if !supported_http_versions.contains(&http_version.to_uppercase().to_string()) {
264            return Err(Request::_ERROR_UNABLE_TO_PARSE_METHOD_AND_REQUEST_URI_AND_HTTP_VERSION.to_string())
265        }
266
267        Ok((method.to_string(), request_uri.to_string(), http_version.to_string()))
268
269    }
270
271    pub fn parse_http_request_header_string(header_string: &str) -> Header {
272        let header_parts: Vec<&str> = header_string.split(Header::NAME_VALUE_SEPARATOR).collect();
273        let header_name = StringExt::truncate_new_line_carriage_return(header_parts[0]);
274        let mut header_value= "".to_string();
275        if header_parts.get(1).is_some() {
276            header_value = StringExt::truncate_new_line_carriage_return(header_parts[1]);
277        }
278
279        Header {
280            name: header_name,
281            value: header_value,
282        }
283    }
284
285    pub fn cursor_read(cursor: &mut Cursor<&[u8]>, mut iteration_number: usize, request: &mut Request, mut content_length: usize) -> Result<bool, String> {
286        let mut buf = vec![];
287        let bytes_offset = cursor.read_until(b'\n', &mut buf).unwrap();
288        let b : &[u8] = &buf;
289        let boxed_request = String::from_utf8(Vec::from(b));
290        if boxed_request.is_err() {
291            let error_message = boxed_request.err().unwrap().to_string();
292            return Err(error_message);
293        }
294        let string = boxed_request.unwrap();
295
296        let is_first_iteration = iteration_number == 0;
297        let new_line_char_found = bytes_offset != 0;
298        let current_string_is_empty = string.trim().len() == 0;
299
300        if is_first_iteration {
301            match Request::parse_method_and_request_uri_and_http_version_string(&string) {
302                Ok((method, request_uri, http_version)) => {
303                    request.method = method;
304                    request.request_uri = request_uri;
305                    request.http_version = http_version;
306                }
307                Err(error_message) => {
308                    return Err(error_message)
309                }
310            }
311        }
312
313        if current_string_is_empty {
314            return Ok(true);
315        }
316
317        if new_line_char_found && !current_string_is_empty {
318            let mut header = Header { name: "".to_string(), value: "".to_string() };
319            if !is_first_iteration {
320                header = Request::parse_http_request_header_string(&string);
321                if header.name == Header::_CONTENT_LENGTH {
322                    content_length = header.value.parse().unwrap();
323                }
324            }
325
326            request.headers.push(header);
327            iteration_number += 1;
328            let boxed_read = Request::cursor_read(cursor, iteration_number, request, content_length);
329            if boxed_read.is_err() {
330                let reason = boxed_read.err().unwrap().to_string();
331                eprintln!("unable to read request: {}", reason);
332            }
333        }
334
335        // remaining part is request body
336
337        let mut buf = vec![];
338        let _ = cursor.read_to_end(&mut buf).unwrap();
339        let b : &[u8] = &buf;
340
341        request.body.append(&mut Vec::from(b));// = Vec::from(b);
342
343        Ok(true)
344    }
345
346}
347
348impl std::fmt::Display for Request {
349    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
350        write!(fmt, "Request method {} and request uri {} and http_version {}", self.method, self.request_uri, self.http_version)
351    }
352}