1use crate::types::HttpMethod;
2
3use std::collections::HashMap;
4
5pub fn parse_headers(
6 request_string: String,
7) -> Result<
8 (
9 (String, String, String),
10 HashMap<String, String>,
11 String,
12 String,
13 Option<HashMap<String, String>>,
14 ),
15 String,
16> {
17 let mut parts = request_string.splitn(2, "\r\n\r\n");
18 let headers_str = parts.next().ok_or("")?;
19 let body_str = parts.next().unwrap_or("");
20
21 let mut headers: HashMap<String, String> = HashMap::new();
22 let mut first_line = None;
23
24 for (i, line) in headers_str.lines().enumerate() {
25 if i == 0 {
26 first_line = Some(line.to_owned());
27 continue;
28 }
29 let mut parts = line.splitn(2, ": ");
30 let key = parts
31 .next()
32 .ok_or(format!("Invalid header: {}", line))?
33 .to_owned();
34 let value = parts
35 .next()
36 .ok_or(format!("Invalid header: {}", line))?
37 .to_owned();
38 headers.insert(key, value);
39 }
40 let first_line = first_line.ok_or("Invalid request: no first line found")?;
41 let mut first_line_parts = first_line.split_whitespace();
42
43 let method = first_line_parts
44 .next()
45 .ok_or("Invalid request: no method found")?
46 .to_owned();
47 let path = first_line_parts
48 .next()
49 .ok_or("Invalid request: no path found")?
50 .to_owned();
51 let http_version = first_line_parts
52 .next()
53 .ok_or("Invalid request: no HTTP version found")?
54 .to_owned();
55
56 let mut query_params = None;
57 if let Some(pos) = path.find('?') {
58 let query_string = &path[pos + 1..];
59 query_params = Some(parse_query_params(query_string));
60 let path_without_query = path[..pos].to_owned();
62 return Ok((
63 (method.to_string(), path_without_query, http_version),
64 headers,
65 headers_str.to_string(),
66 body_str.to_string(),
67 query_params,
68 ));
69 }
70
71 let mut params = HashMap::new();
72 if let Some(query_params) = query_params {
73 for (key, value) in query_params.iter() {
74 params.insert(key.to_string(), value.to_string());
75 }
76 }
77
78 Ok((
79 (method.to_string(), path.to_string(), http_version),
80 headers,
81 headers_str.to_string(),
82 body_str.to_string(),
83 Some(params),
84 ))
85}
86
87fn parse_query_params(query_string: &str) -> HashMap<String, String> {
88 let mut result = HashMap::new();
89 for pair in query_string.split('&') {
90 if let Some((name, value)) = parse_query_pair(pair) {
91 result.insert(name, value);
92 }
93 }
94 result
95}
96
97fn parse_query_pair(pair: &str) -> Option<(String, String)> {
98 let mut parts = pair.split('=');
99 let name = parts.next()?.to_string();
100 let value = parts.next()?.to_string();
101 Some((name, value))
102}
103
104#[derive(Debug)]
105pub struct Request {
106 pub path: String,
107 pub method: HttpMethod,
108 pub version: String,
109 pub headers: HashMap<String, String>,
110 pub headers_raw: String,
111 pub body: String,
112 pub raw_string: String,
113 pub queries: Option<HashMap<String, String>>,
114 pub params: HashMap<String, String>,
115}
116impl Request {
117 pub fn new(request_string: String) -> Request {
118 let (first_line, headers, headers_str, body, queries) =
119 parse_headers(request_string.to_string()).unwrap();
120 return Request {
121 method: HttpMethod::from_string(first_line.0.as_str()),
122 path: first_line.1,
123 version: first_line.2,
124 headers: headers,
125 headers_raw: headers_str,
126 body: body,
127 raw_string: request_string,
128 queries: queries,
129 params: HashMap::new(),
130 };
131 }
132
133 pub fn get_query(&self, query_name: &str) -> Option<String> {
134 if let Some(query_params) = &self.queries {
135 if let Some(query_value) = query_params.get(query_name) {
136 return Some(query_value.to_owned());
137 }
138 }
139 None
140 }
141}