1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
//! HTTP request parsing

use crate::byte::{split, splitn};
use crate::http::server::HttpSettings;
use crate::http::ReadWrite;
use crate::{Fail, Result};

use std::{collections::HashMap, net::SocketAddr};

/// HTTP request method (GET or POST)
#[derive(Debug, PartialEq, Eq)]
pub enum HttpMethod {
    Get,
    Post,
    Put,
    Delete,
    Head,
    Connect,
    Options,
    Trace,
}

/// HTTP request structure
#[derive(Debug)]
pub struct HttpRequest<'a> {
    method: HttpMethod,
    url: &'a str,
    headers: HashMap<String, &'a str>,
    get: HashMap<String, &'a str>,
    post: HashMap<String, Vec<u8>>,
    ip: String,
    body: Vec<u8>,
}

impl<'a> HttpRequest<'a> {
    /// Get HTTP request method
    pub fn method(&self) -> &HttpMethod {
        // return HTTP request method
        &self.method
    }

    /// Get URL
    pub fn url(&self) -> &str {
        // return URL
        self.url
    }

    /// Get headers map
    pub fn headers(&self) -> &HashMap<String, &str> {
        // return headers map
        &self.headers
    }

    /// Get GET parameters
    pub fn get(&self) -> &HashMap<String, &str> {
        // return GET parameters map
        &self.get
    }

    /// Get POST parameters
    pub fn post(&self) -> &HashMap<String, Vec<u8>> {
        // return POST parameters map
        &self.post
    }

    /// Get POST parameters
    pub fn post_utf8(&self) -> HashMap<String, String> {
        // init map and iterate through byte map
        let mut post_utf8 = HashMap::new();
        for (k, v) in &self.post {
            // parse and insert
            post_utf8.insert(k.to_string(), String::from_utf8_lossy(v).to_string());
        }

        // return new UTF-8 POST parameters map
        post_utf8
    }

    /// Get body
    pub fn body(&self) -> &[u8] {
        // return body string
        &self.body
    }

    /// Get IP address
    pub fn ip(&self) -> &str {
        // return IP address string
        &self.ip
    }

    /// Parse HTTP request
    pub fn from(
        raw_header: &'a str,
        mut partial_body: Vec<u8>,
        stream: &mut impl ReadWrite,
        address: SocketAddr,
        settings: &HttpSettings,
    ) -> Result<Self> {
        // split header
        let mut header = raw_header.lines();
        let mut reqln = header
            .next()
            .ok_or_else(|| Fail::new("Empty header"))?
            .split(' ');

        // parse method
        let method = match reqln
            .next()
            .ok_or_else(|| Fail::new("No method in header"))?
        {
            "GET" => Ok(HttpMethod::Get),
            "POST" => Ok(HttpMethod::Post),
            "PUT" => Ok(HttpMethod::Put),
            "DELETE" => Ok(HttpMethod::Delete),
            "HEAD" => Ok(HttpMethod::Head),
            "CONNECT" => Ok(HttpMethod::Connect),
            "OPTIONS" => Ok(HttpMethod::Options),
            "TRACE" => Ok(HttpMethod::Trace),
            _ => Fail::from("Invalid method in header"),
        }?;

        // parse url and split raw get parameters
        let mut get_raw = "";
        let url = if let Some(full_url) = reqln.next() {
            let mut split_url = full_url.splitn(2, '?');
            let url = split_url
                .next()
                .ok_or_else(|| Fail::new("No URL in header"))?;
            if let Some(params) = split_url.next() {
                get_raw = params;
            }
            url
        } else {
            "/"
        };

        // parse headers
        let mut headers = HashMap::new();
        header.for_each(|hl| {
            let mut hls = hl.splitn(2, ':');
            if let (Some(key), Some(value)) = (hls.next(), hls.next()) {
                headers.insert(key.trim().to_lowercase(), value.trim());
            }
        });

        // get content length
        let buf_len = if let Some(buf_len) = headers.get("Content-Length") {
            Some(buf_len)
        } else {
            headers.get("content-length")
        };

        // read rest of body
        if let Some(buf_len) = buf_len {
            // parse buffer length
            let con_len = buf_len
                .parse::<usize>()
                .ok()
                .ok_or_else(|| Fail::new("Content-Length is not of type usize"))?;

            // check if body size is ok.
            if con_len > settings.max_body_size {
                return Fail::from("Max body size exceeded");
            }

            // read body
            let mut read_fails = 0;
            while partial_body.len() < con_len {
                // read next buffer
                let mut rest_body = vec![0u8; settings.body_buffer];
                let length = stream
                    .read(&mut rest_body)
                    .ok()
                    .ok_or_else(|| Fail::new("Stream broken"))?;
                rest_body.truncate(length);
                partial_body.append(&mut rest_body);

                // check if didn't read fully
                if length < settings.body_buffer {
                    read_fails += 1;

                    // failed too often
                    if read_fails > settings.body_read_attempts {
                        return Fail::from("Read body failed too often");
                    }
                }
            }
        }

        // parse GET and POST parameters
        let get = parse_parameters(get_raw, |v| v)?;
        let post = parse_post(&headers, &partial_body).unwrap_or_default();

        // ip: x-real-ip if socket ip is loopback else socket ip
        let ip = match headers.get("x-real-ip") {
            Some(x_real_ip) if address.ip().is_loopback() => x_real_ip.to_string(),
            _ => address.ip().to_string(),
        };

        Ok(Self {
            method,
            url,
            headers,
            get,
            post,
            ip,
            body: partial_body,
        })
    }
}

/// Parse POST parameters to map
fn parse_post(headers: &HashMap<String, &str>, body: &[u8]) -> Result<HashMap<String, Vec<u8>>> {
    match headers.get("content-type") {
        Some(&content_type_header) => {
            let mut content_type_header = content_type_header.split(';').map(|s| s.trim());
            let mut content_type = None;
            let boundary = content_type_header.find_map(|s| {
                if s.starts_with("boundary=") {
                    return s.split('=').nth(1);
                } else if content_type.is_none() {
                    content_type = Some(s);
                }
                None
            });
            match content_type {
                Some(content_type) => {
                    if content_type == "multipart/form-data" {
                        parse_post_upload(
                            body,
                            boundary.ok_or_else(|| Fail::new("post upload, but no boundary"))?,
                        )
                    } else {
                        parse_parameters(&String::from_utf8(body.to_vec())?, |v| {
                            v.as_bytes().to_vec()
                        })
                    }
                }
                None => parse_parameters(&String::from_utf8(body.to_vec())?, |v| {
                    v.as_bytes().to_vec()
                }),
            }
        }
        None => parse_parameters(&String::from_utf8(body.to_vec())?, |v| {
            v.as_bytes().to_vec()
        }),
    }
}

/// Parse POST upload to map
fn parse_post_upload(body: &[u8], boundary: &str) -> Result<HashMap<String, Vec<u8>>> {
    // parameters map
    let mut params = HashMap::new();

    // split body into sections
    let mut sections = split(&body, &format!("--{boundary}\r\n"));
    sections.remove(0);
    for mut section in sections {
        // check if last section
        let last_sep = format!("--{boundary}--\r\n");
        if section.ends_with(last_sep.as_bytes()) {
            // remove ending seperator from last section
            section = &section[..(section.len() - last_sep.len() - 2)];
        }
        // split lines (max 3)
        let lines = splitn(3, &section, b"\r\n");

        // parse name
        let name = String::from_utf8_lossy(lines[0])
            .split(';')
            .map(|s| s.trim())
            .find_map(|s| {
                if s.starts_with("name=") {
                    let name = s.split('=').nth(1)?;
                    Some(name[1..(name.len() - 1)].to_lowercase())
                } else {
                    None
                }
            })
            .ok_or_else(|| Fail::new("missing name in post body section"))?;

        // get value
        let data_section = lines
            .get(2)
            .ok_or_else(|| Fail::new("broken section in post body"))?;
        let data_lines = splitn(2, data_section, b"\r\n");
        let next_data_line = data_lines
            .first()
            .ok_or_else(|| Fail::new("broken section in post body"))?;
        let value = if let Some(file_data_line) = data_lines.get(1) {
            if next_data_line.is_empty() {
                file_data_line.to_vec()
            } else if file_data_line.is_empty() {
                next_data_line.to_vec()
            } else {
                [&next_data_line[..], &b"\r\n"[..], &file_data_line[..]]
                    .concat()
                    .to_vec()
            }
        } else {
            next_data_line.to_vec()
        };

        // insert into map
        params.insert(name, value);
    }

    // return parameters map
    Ok(params)
}

/// Parse GET parameters to map
fn parse_parameters<'a, V>(
    raw: &'a str,
    process_value: fn(&'a str) -> V,
) -> Result<HashMap<String, V>> {
    // parameters map
    let mut params = HashMap::new();

    // split parameters by ampersand
    for p in raw.split('&') {
        // split key and value and add to map
        let mut ps = p.splitn(2, '=');
        params.insert(
            ps.next()
                .ok_or_else(|| Fail::new("broken x-www-form-urlencoded parameters"))?
                .trim()
                .to_lowercase(), // trimmed key
            // correct value type
            process_value(if let Some(value) = ps.next() {
                value.trim() // trimmed value
            } else {
                "" // no value, is option
            }),
        );
    }

    // return parameters map
    Ok(params)
}