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::http::HTTP;
11use crate::symbol::SYMBOL;
12use crate::url::URL;
13
14#[derive(PartialEq, Eq, Clone, Debug)]
16pub struct Request {
17 pub method: String,
19 pub request_uri: String,
21 pub http_version: String,
23 pub headers: Vec<Header>,
25 pub body: Vec<u8>,
27}
28
29#[derive(PartialEq, Eq, Clone, Debug)]
30pub struct Method {
31 pub get: &'static str,
32 pub head: &'static str,
33 pub post: &'static str,
34 pub put: &'static str,
35 pub delete: &'static str,
36 pub connect: &'static str,
37 pub options: &'static str,
38 pub trace: &'static str,
39 pub patch: &'static str,
40}
41
42pub const METHOD: Method = Method {
43 get: "GET",
44 head: "HEAD",
45 post: "POST",
46 put: "PUT",
47 delete: "DELETE",
48 connect: "CONNECT",
49 options: "OPTIONS",
50 trace: "TRACE",
51 patch: "PATCH",
52};
53
54impl Request {
55 pub const _ERROR_UNABLE_TO_PARSE_METHOD_AND_REQUEST_URI_AND_HTTP_VERSION: &'static str = "Unable to parse method, request uri and http version";
56
57 pub fn get_header(&self, name: String) -> Option<&Header> {
58 let header = self.headers.iter().find(|x| x.name.to_lowercase() == name.to_lowercase());
59 header
60 }
61
62 pub fn get_domain(&self) -> Result<Option<String>, String> {
63 let boxed_host = &self.get_header(Header::_HOST.to_string());
64 if boxed_host.is_none() {
65 return Ok(None);
66 }
67
68 let host_header = boxed_host.unwrap();
69 let host = host_header.value.to_string();
70 if host.is_empty() {
71 return Ok(None); }
73
74 let boxed_host_port = host.split_once(&SYMBOL.colon.to_string());
75 if boxed_host_port.is_none() {
76 return Ok(Some(host))
77 }
78
79 let (domain, _port_str) = boxed_host_port.unwrap();
80
81 Ok(Some(domain.to_string()))
82 }
83
84 pub fn get_port(&self) -> Result<Option<i128>, String> {
85 let boxed_host = &self.get_header(Header::_HOST.to_string());
86 if boxed_host.is_none() {
87 return Ok(None);
88 }
89
90 let host_header = boxed_host.unwrap();
91 let host = host_header.value.to_string();
92
93 let boxed_host_port = host.split_once(&SYMBOL.colon.to_string());
94 if boxed_host_port.is_none() {
95 return Ok(None)
96 }
97
98 let (_domain, port_str) = boxed_host_port.unwrap();
99
100 let boxed_port_i128 = port_str.parse::<i128>();
101 if boxed_port_i128.is_err() {
102 let message = boxed_port_i128.err().unwrap().to_string();
103 return Err(message);
104 }
105
106 let port = boxed_port_i128.unwrap();
107
108 Ok(Some(port))
109 }
110
111 pub fn get_query(&self) -> Result<Option<HashMap<String, String>>, String> {
112 self.get_uri_query()
113 }
114
115 pub fn get_uri_query(&self) -> Result<Option<HashMap<String, String>>, String> {
116 let url_array = ["http://", "localhost/", &self.request_uri];
120 let url = url_array.join(SYMBOL.empty_string);
121
122 let boxed_url_components = URL::parse(&url);
123 if boxed_url_components.is_err() {
124 let message = boxed_url_components.err().unwrap().to_string();
125 return Err(message)
126 }
127 Ok(boxed_url_components.unwrap().query)
128 }
129
130 pub fn get_path(&self) -> Result<String, String> {
131 self.get_uri_path()
132 }
133
134 pub fn get_uri_path(&self) -> Result<String, String> {
135 let url = self.request_uri.to_string();
137
138 let boxed_url_components = crate::url::URL::parse_request_target(&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 is_supported_method(method: &str) -> bool {
149 [METHOD.get, METHOD.head, METHOD.post, METHOD.put, METHOD.delete, METHOD.connect, METHOD.options, METHOD.trace, METHOD.patch]
150 .iter()
151 .any(|m| m.eq_ignore_ascii_case(method))
152 }
153
154 pub fn method_list() -> Vec<String> {
155 let method_get = METHOD.get.to_string();
156 let method_head = METHOD.head.to_string();
157 let method_post = METHOD.post.to_string();
158 let method_put = METHOD.put.to_string();
159 let method_delete = METHOD.delete.to_string();
160 let method_connect = METHOD.connect.to_string();
161 let method_options = METHOD.options.to_string();
162 let method_trace = METHOD.trace.to_string();
163 let method_patch = METHOD.patch.to_string();
164
165 let method_list = vec![
166 method_get,
167 method_head,
168 method_post,
169 method_put,
170 method_delete,
171 method_connect,
172 method_options,
173 method_trace,
174 method_patch,
175 ];
176
177 method_list
178 }
179
180 pub fn generate(&self) -> Vec<u8> {
182 let clone = self.clone();
183 let mut request = Request::_generate_request(clone).as_bytes().to_vec();
184
185 let mut body = self.body.clone();
186 request.append(&mut body);
187
188 request
189 }
190
191 pub fn generate_request(request: Request) -> String {
193 Request::_generate_request(request)
194 }
195
196 pub fn _generate_request(request: Request) -> String {
197 let status = [
198 request.method,
199 request.request_uri,
200 request.http_version,
201 SYMBOL.new_line_carriage_return.to_string()
202 ].join(SYMBOL.whitespace);
203
204 let mut headers = SYMBOL.empty_string.to_string();
205 for header in request.headers {
206 let mut header_string = SYMBOL.empty_string.to_string();
207 header_string.push_str(&header.name);
208 header_string.push_str(Header::NAME_VALUE_SEPARATOR);
209 header_string.push_str(&header.value);
210 header_string.push_str(SYMBOL.new_line_carriage_return);
211 headers.push_str(&header_string);
212 }
213
214 let request = format!(
215 "{}{}{}",
216 status,
217 headers,
218 SYMBOL.new_line_carriage_return
219 );
220
221
222 request
223 }
224
225 pub fn parse(request_vec_u8: &[u8]) -> Result<Request, String> {
226 Request::parse_request(request_vec_u8)
227 }
228
229 pub fn parse_request(request_vec_u8: &[u8]) -> Result<Request, String> {
230 let mut cursor = io::Cursor::new(request_vec_u8);
231
232 let mut request = Request {
233 method: "".to_string(),
234 request_uri: "".to_string(),
235 http_version: "".to_string(),
236 headers: vec![],
237 body: vec![],
238 };
239
240 let content_length: usize = 0;
241 let iteration_number : usize = 0;
242 return match Request::cursor_read(&mut cursor, iteration_number, &mut request, content_length) {
243 Ok(_) => {
244 Ok(request)
245 }
246 Err(error_message) => {
247 Err(error_message)
248 }
249 }
250
251 }
252
253
254 pub fn parse_method_and_request_uri_and_http_version_string(http_version_status_code_reason_phrase: &str) -> Result<(String, String, String), String> {
255 let lowercase_unparsed_method_and_request_uri_and_http_version = http_version_status_code_reason_phrase.trim();
256
257 let boxed_split_without_method = lowercase_unparsed_method_and_request_uri_and_http_version.split_once(SYMBOL.whitespace);
258 if boxed_split_without_method.is_none() {
259 return Err(Request::_ERROR_UNABLE_TO_PARSE_METHOD_AND_REQUEST_URI_AND_HTTP_VERSION.to_string())
260 }
261
262 let (method, without_method) = boxed_split_without_method.unwrap();
263 if !Request::is_supported_method(method) {
264 return Err(Request::_ERROR_UNABLE_TO_PARSE_METHOD_AND_REQUEST_URI_AND_HTTP_VERSION.to_string())
265 }
266
267 let boxed_without_method = without_method.split_once(SYMBOL.whitespace);
268 if boxed_without_method.is_none() {
269 return Err(Request::_ERROR_UNABLE_TO_PARSE_METHOD_AND_REQUEST_URI_AND_HTTP_VERSION.to_string())
270 }
271
272 let (request_uri, http_version) = boxed_without_method.unwrap();
273 let request_uri = Request::normalize_request_target(request_uri)
274 .ok_or_else(|| Request::_ERROR_UNABLE_TO_PARSE_METHOD_AND_REQUEST_URI_AND_HTTP_VERSION.to_string())?;
275
276
277 if !HTTP::is_supported_version(http_version) {
278 return Err(Request::_ERROR_UNABLE_TO_PARSE_METHOD_AND_REQUEST_URI_AND_HTTP_VERSION.to_string())
279 }
280
281 Ok((method.to_string(), request_uri, http_version.to_string()))
282
283 }
284
285 pub fn normalize_request_target(target: &str) -> Option<String> {
291 if target.starts_with('/') || target == "*" {
292 return Some(target.to_string());
293 }
294 let lower = target.to_ascii_lowercase();
295 let after_scheme = if lower.starts_with("http://") {
296 &target["http://".len()..]
297 } else if lower.starts_with("https://") {
298 &target["https://".len()..]
299 } else {
300 return None;
301 };
302 let path_start = after_scheme.find(|c| c == '/' || c == '?').unwrap_or(after_scheme.len());
303 if path_start == 0 {
304 return None; }
306 let rest = &after_scheme[path_start..];
307 Some(if rest.starts_with('/') { rest.to_string() } else { format!("/{}", rest) })
308 }
309
310 pub fn parse_http_request_header_string(header_string: &str) -> Header {
315 let line = header_string.trim_end_matches(['\r', '\n']);
316 match line.split_once(':') {
317 Some((name, value)) => Header {
318 name: name.to_string(),
319 value: value.trim_matches([' ', '\t']).to_string(),
320 },
321 None => Header { name: line.to_string(), value: String::new() },
322 }
323 }
324
325 pub fn cursor_read(cursor: &mut Cursor<&[u8]>, mut iteration_number: usize, request: &mut Request, content_length: usize) -> Result<bool, String> {
333 let _ = content_length;
334 loop {
335 let mut buf = vec![];
336 let bytes_offset = cursor.read_until(b'\n', &mut buf).map_err(|e| e.to_string())?;
337 let string = String::from_utf8(buf).map_err(|e| e.to_string())?;
338
339 let is_first_iteration = iteration_number == 0;
340 if is_first_iteration {
341 let (method, request_uri, http_version) =
342 Request::parse_method_and_request_uri_and_http_version_string(&string)?;
343 request.method = method;
344 request.request_uri = request_uri;
345 request.http_version = http_version;
346 }
347
348 if bytes_offset == 0 || string.trim().is_empty() {
350 break;
351 }
352
353 if !is_first_iteration {
357 let header = Request::parse_http_request_header_string(&string);
358 if header.name.eq_ignore_ascii_case(Header::_CONTENT_LENGTH) {
359 let _: usize = header.value.trim().parse()
361 .map_err(|_| format!("invalid Content-Length: {:?}", header.value))?;
362 }
363 request.headers.push(header);
364 }
365 iteration_number += 1;
366 }
367
368 let mut body = vec![];
370 cursor.read_to_end(&mut body).map_err(|e| e.to_string())?;
371 request.body.append(&mut body);
372
373 Ok(true)
374 }
375
376}
377
378impl std::fmt::Display for Request {
379 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
380 write!(fmt, "Request method {} and request uri {} and http_version {}", self.method, self.request_uri, self.http_version)
381 }
382}