torus_http/
request.rs

1//! This module handles parsing the client's request into a simple to work with data structure
2//!
3//! missing query params though
4use std::{collections::HashMap, str::FromStr};
5
6use crate::method::HttpMethod;
7
8#[derive(Clone, Debug, PartialEq, Eq)]
9/// The incoming request
10pub struct Request {
11    /// i.e. Get, Post, etc...
12    pub method: HttpMethod,
13    /// path, currently including query parameters in the string
14    pub path: String,
15    pub headers: HashMap<String, String>,
16    pub body: Option<String>,
17}
18
19impl FromStr for Request {
20    type Err = std::io::Error;
21
22    fn from_str(s: &str) -> Result<Self, Self::Err> {
23        let first_line = s
24            .lines()
25            .next()
26            .ok_or({
27                std::io::Error::new(
28                    std::io::ErrorKind::InvalidInput,
29                    "failed getting first http line",
30                )
31            })?
32            .split_whitespace()
33            .collect::<Vec<&str>>();
34        let (method, path): (HttpMethod, String) = match first_line.as_slice() {
35            [method_str, path, _version] => {
36                (HttpMethod::from_str_val(method_str), (*path).to_string())
37            }
38            _ => {
39                return Err(std::io::Error::new(
40                    std::io::ErrorKind::InvalidInput,
41                    "invalid http top header",
42                ));
43            }
44        };
45
46        let headers: HashMap<String, String> = s
47            .lines()
48            .take_while(|line| !line.is_empty())
49            .skip(1)
50            .filter_map(|line| {
51                line.split_once(':')
52                    .map(|(k, v)| (k.trim().to_string(), v.trim().to_string()))
53            })
54            .collect();
55
56        let body = if method == HttpMethod::Get {
57            None
58        } else {
59            s.split_once("\r\n\r\n")
60                .map(|v| v.1.to_owned())
61                .filter(|v| !v.is_empty())
62        };
63
64        let req: Request = Request {
65            method,
66            path,
67            headers,
68            body,
69        };
70        Ok(req)
71    }
72}