Skip to main content

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