1use std::collections::HashMap;
2use std::io::{self, BufRead, BufReader, Read, Write};
3
4pub struct HttpRequest {
6 pub method: String,
7 pub path: String,
8 pub query: String,
9 pub headers: HashMap<String, String>,
10 pub body: Vec<u8>,
11}
12
13pub struct HttpResponse {
15 pub status: u16,
16 pub status_text: &'static str,
17 pub headers: Vec<(String, String)>,
18 pub body: Vec<u8>,
19}
20
21impl HttpResponse {
22 pub fn json(status: u16, status_text: &'static str, body: &serde_json::Value) -> Self {
23 let body_bytes = serde_json::to_vec(body).unwrap_or_default();
24 HttpResponse {
25 status,
26 status_text,
27 headers: vec![
28 ("Content-Type".into(), "application/json".into()),
29 ("Content-Length".into(), body_bytes.len().to_string()),
30 ("Connection".into(), "close".into()),
31 ],
32 body: body_bytes,
33 }
34 }
35
36 pub fn ok(body: serde_json::Value) -> Self {
37 Self::json(200, "OK", &body)
38 }
39
40 pub fn html(body: &str) -> Self {
41 Self::bytes(
42 200,
43 "OK",
44 "text/html; charset=utf-8",
45 body.as_bytes().to_vec(),
46 )
47 }
48
49 pub fn created(body: serde_json::Value) -> Self {
50 Self::json(201, "Created", &body)
51 }
52
53 pub fn bad_request(msg: &str) -> Self {
54 Self::json(400, "Bad Request", &serde_json::json!({"error": msg}))
55 }
56
57 pub fn unauthorized(msg: &str) -> Self {
58 Self::json(401, "Unauthorized", &serde_json::json!({"error": msg}))
59 }
60
61 pub fn not_found() -> Self {
62 Self::json(404, "Not Found", &serde_json::json!({"error": "Not found"}))
63 }
64
65 pub fn internal_error(msg: &str) -> Self {
66 Self::json(
67 500,
68 "Internal Server Error",
69 &serde_json::json!({"error": msg}),
70 )
71 }
72
73 pub fn bytes(
74 status: u16,
75 status_text: &'static str,
76 content_type: &'static str,
77 body: Vec<u8>,
78 ) -> Self {
79 HttpResponse {
80 status,
81 status_text,
82 headers: vec![
83 ("Content-Type".into(), content_type.into()),
84 ("Content-Length".into(), body.len().to_string()),
85 ("Connection".into(), "close".into()),
86 ],
87 body,
88 }
89 }
90}
91
92pub fn parse_request(stream: &mut dyn Read) -> io::Result<HttpRequest> {
94 let mut reader = BufReader::new(stream);
95
96 let mut request_line = String::new();
98 reader.read_line(&mut request_line)?;
99 let request_line = request_line.trim_end();
100
101 if request_line.is_empty() {
102 return Err(io::Error::new(
103 io::ErrorKind::UnexpectedEof,
104 "Empty request",
105 ));
106 }
107
108 let parts: Vec<&str> = request_line.splitn(3, ' ').collect();
109 if parts.len() < 2 {
110 return Err(io::Error::new(
111 io::ErrorKind::InvalidData,
112 "Invalid request line",
113 ));
114 }
115
116 let method = parts[0].to_string();
117 let full_path = parts[1];
118
119 let (path, query) = if let Some(pos) = full_path.find('?') {
120 (
121 full_path[..pos].to_string(),
122 full_path[pos + 1..].to_string(),
123 )
124 } else {
125 (full_path.to_string(), String::new())
126 };
127
128 let mut headers = HashMap::new();
130 loop {
131 let mut line = String::new();
132 reader.read_line(&mut line)?;
133 let line = line.trim_end();
134 if line.is_empty() {
135 break;
136 }
137 if let Some(colon) = line.find(':') {
138 let key = line[..colon].trim().to_lowercase();
139 let value = line[colon + 1..].trim().to_string();
140 headers.insert(key, value);
141 }
142 }
143
144 let body = if let Some(len_str) = headers.get("content-length") {
146 if let Ok(len) = len_str.parse::<usize>() {
147 let mut body = vec![0u8; len];
148 reader.read_exact(&mut body)?;
149 body
150 } else {
151 Vec::new()
152 }
153 } else {
154 Vec::new()
155 };
156
157 Ok(HttpRequest {
158 method,
159 path,
160 query,
161 headers,
162 body,
163 })
164}
165
166pub fn write_response(stream: &mut dyn Write, response: &HttpResponse) -> io::Result<()> {
168 write!(
169 stream,
170 "HTTP/1.1 {} {}\r\n",
171 response.status, response.status_text
172 )?;
173 for (key, value) in &response.headers {
174 write!(stream, "{}: {}\r\n", key, value)?;
175 }
176 write!(stream, "\r\n")?;
177 stream.write_all(&response.body)?;
178 stream.flush()
179}
180
181pub fn parse_query(query: &str) -> HashMap<String, String> {
183 let mut params = HashMap::new();
184 if query.is_empty() {
185 return params;
186 }
187 for pair in query.split('&') {
188 if let Some(eq) = pair.find('=') {
189 let key = pair[..eq].to_string();
190 let value = pair[eq + 1..].to_string();
191 params.insert(key, value);
192 } else if !pair.is_empty() {
193 params.insert(pair.to_string(), String::new());
194 }
195 }
196 params
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 #[test]
204 fn parse_query_basic() {
205 let q = parse_query("foo=bar&baz=123&flag");
206 assert_eq!(q.get("foo").unwrap(), "bar");
207 assert_eq!(q.get("baz").unwrap(), "123");
208 assert!(q.contains_key("flag"));
209 }
210
211 #[test]
212 fn parse_query_empty() {
213 let q = parse_query("");
214 assert!(q.is_empty());
215 }
216
217 #[test]
218 fn parse_request_get() {
219 let raw = b"GET /api/info?verbose=true HTTP/1.1\r\nHost: localhost\r\nAuthorization: Bearer abc\r\n\r\n";
220 let req = parse_request(&mut &raw[..]).unwrap();
221 assert_eq!(req.method, "GET");
222 assert_eq!(req.path, "/api/info");
223 assert_eq!(req.query, "verbose=true");
224 assert_eq!(req.headers.get("authorization").unwrap(), "Bearer abc");
225 assert!(req.body.is_empty());
226 }
227
228 #[test]
229 fn parse_request_post_with_body() {
230 let body = r#"{"key":"value"}"#;
231 let raw = format!(
232 "POST /api/send HTTP/1.1\r\nContent-Length: {}\r\n\r\n{}",
233 body.len(),
234 body
235 );
236 let req = parse_request(&mut raw.as_bytes()).unwrap();
237 assert_eq!(req.method, "POST");
238 assert_eq!(req.path, "/api/send");
239 assert_eq!(req.body, body.as_bytes());
240 }
241
242 #[test]
243 fn response_json() {
244 let resp = HttpResponse::ok(serde_json::json!({"status": "ok"}));
245 assert_eq!(resp.status, 200);
246 let body: serde_json::Value = serde_json::from_slice(&resp.body).unwrap();
247 assert_eq!(body["status"], "ok");
248 }
249}