1use std::collections::HashMap;
2
3use cookie::Cookie;
4use http::{
5 header::{CONTENT_LENGTH, CONTENT_TYPE, COOKIE},
6 HeaderMap, HeaderValue, Request as HttpRequest, Version,
7};
8use log::warn;
9
10use crate::http::{
11 conn::Conn,
12 utils::{parse_json_body, parse_query, read_headers},
13};
14
15#[derive(Debug)]
17pub struct Request {
18 req: HttpRequest<Vec<u8>>,
19 pub args: HashMap<String, String>,
20 pub data: HashMap<String, String>,
21 pub params: HashMap<String, String>,
22}
23
24impl Request {
25 pub fn new(method: &str, uri: &str, headers: HashMap<String, String>, body: &[u8]) -> Self {
36 let mut builder = HttpRequest::builder().method(method).uri(uri);
37 let content_length = body.len();
38 let mut content_type = String::new();
39 for (key, value) in headers {
40 if key.to_lowercase() == CONTENT_TYPE.as_str().to_lowercase() {
41 content_type = value.clone();
42 }
43 builder = builder.header(key, value);
44 }
45
46 let req = builder
47 .header(CONTENT_LENGTH, body.len())
48 .body(body.to_vec())
49 .unwrap();
50
51 let args = parse_query(req.uri().query());
52 let mut data = HashMap::new();
53 if content_length > 0 {
54 data = match content_type.as_str() {
55 "application/json" => parse_json_body(req.body()),
56 _ => {
57 warn!("unsupported content type {}", content_type);
58 HashMap::new()
59 }
60 };
61 }
62 Self {
63 req,
64 args,
65 data,
66 params: HashMap::new(),
67 }
68 }
69 pub fn from(conn: &mut Conn) -> Self {
71 let mut buf = String::new();
73 conn.read_line(&mut buf);
74 let line: Vec<&str> = buf.trim().split(' ').collect();
75 let (method, uri, version) = (line[0], line[1], line[2]);
76 let version = match version {
77 "HTTP/2.0" => Version::HTTP_2,
78 "HTTP/3.0" => Version::HTTP_3,
79 _ => Version::HTTP_11,
80 };
81 let mut builder = HttpRequest::builder()
82 .method(method)
83 .uri(uri)
84 .version(version);
85
86 let mut content_length = 0;
88 let mut content_type = String::new();
89 for (key, value) in read_headers(conn) {
90 if key.to_lowercase() == CONTENT_LENGTH.as_str().to_lowercase() {
91 content_length = value.parse().unwrap();
92 } else if key.to_lowercase() == CONTENT_TYPE.as_str().to_lowercase() {
93 content_type = value.clone();
94 }
95 builder = builder.header(key, value);
96 }
97
98 let mut body = Vec::<u8>::new();
100 body.resize(content_length, 0);
101 conn.read_exact(&mut body);
102 let req = builder.body(body).unwrap();
103
104 let args = parse_query(req.uri().query());
105 let mut data = HashMap::new();
106 if content_length > 0 {
107 data = match content_type.as_str() {
108 "application/json" => parse_json_body(req.body()),
109 _ => {
110 warn!("unsupported content type {}", content_type);
112 HashMap::new()
113 }
114 };
115 }
116
117 Self {
118 req,
119 args,
120 data,
121 params: HashMap::new(),
122 }
123 }
124
125 pub fn method(&self) -> &str {
127 self.req.method().as_str()
128 }
129
130 pub fn path(&self) -> &str {
132 self.req.uri().path()
133 }
134
135 pub fn full_path(&self) -> &str {
137 if let Some(full_path) = self.req.uri().path_and_query() {
138 return full_path.as_str();
139 }
140 ""
141 }
142
143 pub fn headers(&self) -> &HeaderMap<HeaderValue> {
145 self.req.headers()
146 }
147
148 pub fn cookies(&self) -> HashMap<String, String> {
150 let headers = self.headers();
151 let cookies = headers
152 .get(COOKIE)
153 .map(|v| Cookie::split_parse(v.to_str().unwrap()));
154
155 let mut cookies_map = HashMap::new();
156 for cookie in cookies.into_iter().flatten().flatten() {
157 cookies_map.insert(cookie.name().to_string(), cookie.value().to_string());
158 }
159
160 cookies_map
161 }
162}