maria/
request.rs

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        // remove the query string from the path
61        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}