rust_express/utils/
request.rs

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
use crate::utils::helpers::{parse_body, parse_query_params};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use tokio::io::AsyncReadExt;

#[derive(Deserialize, Serialize, Clone)]
pub struct Request {
    pub method: String,
    pub route: String,
    pub host: String,
    pub user_agent: String,
    pub accept: String,
    pub connection: String,
    pub raw: String,
    pub content_length: usize,
    pub body: HashMap<String, String>,
    pub query: HashMap<String, String>,
}

impl Request {
    fn new() -> Self {
        Self {
            method: "GET".to_owned(),
            route: "/".to_owned(),
            host: "".to_owned(),
            user_agent: "".to_owned(),
            accept: "".to_owned(),
            connection: "keep-alive".to_owned(),
            content_length: 0,
            raw: "".to_owned(),
            body: HashMap::new(),
            query: HashMap::new(),
        }
    }
}

pub async fn parse_request_data(stream: &mut tokio::net::TcpStream) -> Request {
    stream
        .readable()
        .await
        .expect("stream failed to be readable");
    let mut buffer = [0u8; 4096];
    let n = stream
        .read(&mut buffer)
        .await
        .expect("Failed to read request buffer");
    let request_data = String::from_utf8_lossy(&buffer[..n]).to_string();
    let mut request = Request::new();
    request.raw = request_data.clone();
    let mut lines = request_data.split("\r\n");

    if let Some(first_line) = lines.next() {
        let mut parts = first_line.split_whitespace();
        request.method = parts.next().unwrap_or("GET").to_string();
        let full_route = parts.next().unwrap_or("/");
        let mut route_parts = full_route.splitn(2, '?');
        request.route = route_parts.next().unwrap_or("/").to_string();
        let query_string = route_parts.next().unwrap_or("");
        request.query = parse_query_params(query_string);
    }

    for line in lines.by_ref() {
        if line.is_empty() {
            break;
        }

        let mut header_parts = line.splitn(2, ": ");
        let header_key = header_parts.next().unwrap_or("").to_lowercase();
        let header_value = header_parts.next().unwrap_or("").to_string();

        match header_key.as_str() {
            "host" => request.host = header_value,
            "user-agent" => request.user_agent = header_value,
            "accept" => request.accept = header_value,
            "connection" => request.connection = header_value,
            "content-length" => request.content_length = header_value.parse().unwrap_or(0),
            _ => {}
        }
    }

    let body = lines.collect::<Vec<&str>>().join("\r\n");
    let parsed_body = parse_body(&body);
    request.body = parsed_body;
    request
}