kern/http/server/
request.rs1use crate::byte::{split, splitn};
4use crate::http::common::ReadWrite;
5use crate::http::server::HttpSettings;
6use crate::{Fail, Result};
7
8use std::{collections::HashMap, net::SocketAddr};
9
10pub use crate::http::common::HttpMethod;
11
12#[derive(Debug)]
14pub struct HttpRequest<'a> {
15 method: HttpMethod,
16 url: &'a str,
17 headers: HashMap<String, &'a str>,
18 get: HashMap<String, &'a str>,
19 post: HashMap<String, Vec<u8>>,
20 ip: String,
21 body: Vec<u8>,
22}
23
24impl<'a> HttpRequest<'a> {
25 pub fn method(&self) -> &HttpMethod {
27 &self.method
29 }
30
31 pub fn url(&self) -> &str {
33 self.url
35 }
36
37 pub fn headers(&self) -> &HashMap<String, &str> {
39 &self.headers
41 }
42
43 pub fn get(&self) -> &HashMap<String, &str> {
45 &self.get
47 }
48
49 pub fn post(&self) -> &HashMap<String, Vec<u8>> {
51 &self.post
53 }
54
55 pub fn post_utf8(&self) -> HashMap<String, String> {
57 let mut post_utf8 = HashMap::new();
59 for (k, v) in &self.post {
60 post_utf8.insert(k.to_string(), String::from_utf8_lossy(v).to_string());
62 }
63
64 post_utf8
66 }
67
68 pub fn body(&self) -> &[u8] {
70 &self.body
72 }
73
74 pub fn ip(&self) -> &str {
76 &self.ip
78 }
79
80 pub fn from(
82 raw_header: &'a str,
83 mut partial_body: Vec<u8>,
84 stream: &mut impl ReadWrite,
85 address: SocketAddr,
86 settings: &HttpSettings,
87 ) -> Result<Self> {
88 let mut header = raw_header.lines();
90 let mut reqln = header
91 .next()
92 .ok_or_else(|| Fail::new("Empty header"))?
93 .split(' ');
94
95 let method: HttpMethod = reqln
97 .next()
98 .ok_or_else(|| Fail::new("No method in header"))?
99 .try_into()?;
100
101 let mut get_raw = "";
103 let url = if let Some(full_url) = reqln.next() {
104 let mut split_url = full_url.splitn(2, '?');
105 let url = split_url
106 .next()
107 .ok_or_else(|| Fail::new("No URL in header"))?;
108 if let Some(params) = split_url.next() {
109 get_raw = params;
110 }
111 url
112 } else {
113 "/"
114 };
115
116 let mut headers = HashMap::new();
118 header.for_each(|hl| {
119 let mut hls = hl.splitn(2, ':');
120 if let (Some(key), Some(value)) = (hls.next(), hls.next()) {
121 headers.insert(key.trim().to_lowercase(), value.trim());
122 }
123 });
124
125 let buf_len = if let Some(buf_len) = headers.get("Content-Length") {
127 Some(buf_len)
128 } else {
129 headers.get("content-length")
130 };
131
132 if let Some(buf_len) = buf_len {
134 let con_len = buf_len
136 .parse::<usize>()
137 .ok()
138 .ok_or_else(|| Fail::new("Content-Length is not of type usize"))?;
139
140 if con_len > settings.max_body_size {
142 return Fail::from("Max body size exceeded");
143 }
144
145 let mut read_fails = 0;
147 while partial_body.len() < con_len {
148 let mut rest_body = vec![0u8; settings.body_buffer];
150 let length = stream
151 .read(&mut rest_body)
152 .ok()
153 .ok_or_else(|| Fail::new("Stream broken"))?;
154 rest_body.truncate(length);
155 partial_body.append(&mut rest_body);
156
157 if length < settings.body_buffer {
159 read_fails += 1;
160
161 if read_fails > settings.body_read_attempts {
163 return Fail::from("Read body failed too often");
164 }
165 }
166 }
167 }
168
169 let get = parse_parameters(get_raw, |v| v)?;
171 let post = parse_post(&headers, &partial_body).unwrap_or_default();
172
173 let ip = match headers.get("x-real-ip") {
175 Some(x_real_ip) if address.ip().is_loopback() => x_real_ip.to_string(),
176 _ => address.ip().to_string(),
177 };
178
179 Ok(Self {
180 method,
181 url,
182 headers,
183 get,
184 post,
185 ip,
186 body: partial_body,
187 })
188 }
189}
190
191fn parse_post(headers: &HashMap<String, &str>, body: &[u8]) -> Result<HashMap<String, Vec<u8>>> {
193 match headers.get("content-type") {
194 Some(&content_type_header) => {
195 let mut content_type_header = content_type_header.split(';').map(|s| s.trim());
196 let mut content_type = None;
197 let boundary = content_type_header.find_map(|s| {
198 if s.starts_with("boundary=") {
199 return s.split('=').nth(1);
200 } else if content_type.is_none() {
201 content_type = Some(s);
202 }
203 None
204 });
205 match content_type {
206 Some(content_type) => {
207 if content_type == "multipart/form-data" {
208 parse_post_upload(
209 body,
210 boundary.ok_or_else(|| Fail::new("post upload, but no boundary"))?,
211 )
212 } else {
213 parse_parameters(&String::from_utf8(body.to_vec())?, |v| {
214 v.as_bytes().to_vec()
215 })
216 }
217 }
218 None => parse_parameters(&String::from_utf8(body.to_vec())?, |v| {
219 v.as_bytes().to_vec()
220 }),
221 }
222 }
223 None => parse_parameters(&String::from_utf8(body.to_vec())?, |v| {
224 v.as_bytes().to_vec()
225 }),
226 }
227}
228
229fn parse_post_upload(body: &[u8], boundary: &str) -> Result<HashMap<String, Vec<u8>>> {
231 let mut params = HashMap::new();
233
234 let mut sections = split(&body, format!("--{boundary}\r\n"));
236 sections.remove(0);
237 for mut section in sections {
238 let last_sep = format!("--{boundary}--\r\n");
240 if section.ends_with(last_sep.as_bytes()) {
241 section = §ion[..(section.len() - last_sep.len() - 2)];
243 }
244 let lines = splitn(3, §ion, b"\r\n");
246
247 let name = String::from_utf8_lossy(lines[0])
249 .split(';')
250 .map(|s| s.trim())
251 .find_map(|s| {
252 if s.starts_with("name=") {
253 let name = s.split('=').nth(1)?;
254 Some(name[1..(name.len() - 1)].to_lowercase())
255 } else {
256 None
257 }
258 })
259 .ok_or_else(|| Fail::new("missing name in post body section"))?;
260
261 let data_section = lines
263 .get(2)
264 .ok_or_else(|| Fail::new("broken section in post body"))?;
265 let data_lines = splitn(2, data_section, b"\r\n");
266 let next_data_line = data_lines
267 .first()
268 .ok_or_else(|| Fail::new("broken section in post body"))?;
269 let value = if let Some(file_data_line) = data_lines.get(1) {
270 if next_data_line.is_empty() {
271 file_data_line.to_vec()
272 } else if file_data_line.is_empty() {
273 next_data_line.to_vec()
274 } else {
275 [&next_data_line[..], &b"\r\n"[..], &file_data_line[..]]
276 .concat()
277 .to_vec()
278 }
279 } else {
280 next_data_line.to_vec()
281 };
282
283 params.insert(name, value);
285 }
286
287 Ok(params)
289}
290
291fn parse_parameters<'a, V>(
293 raw: &'a str,
294 process_value: fn(&'a str) -> V,
295) -> Result<HashMap<String, V>> {
296 let mut params = HashMap::new();
298
299 for p in raw.split('&') {
301 let mut ps = p.splitn(2, '=');
303 params.insert(
304 ps.next()
305 .ok_or_else(|| Fail::new("broken x-www-form-urlencoded parameters"))?
306 .trim()
307 .to_lowercase(), process_value(if let Some(value) = ps.next() {
310 value.trim() } else {
312 "" }),
314 );
315 }
316
317 Ok(params)
319}