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 created(body: serde_json::Value) -> Self {
41 Self::json(201, "Created", &body)
42 }
43
44 pub fn bad_request(msg: &str) -> Self {
45 Self::json(400, "Bad Request", &serde_json::json!({"error": msg}))
46 }
47
48 pub fn unauthorized(msg: &str) -> Self {
49 Self::json(401, "Unauthorized", &serde_json::json!({"error": msg}))
50 }
51
52 pub fn not_found() -> Self {
53 Self::json(404, "Not Found", &serde_json::json!({"error": "Not found"}))
54 }
55
56 pub fn internal_error(msg: &str) -> Self {
57 Self::json(
58 500,
59 "Internal Server Error",
60 &serde_json::json!({"error": msg}),
61 )
62 }
63}
64
65pub fn parse_request(stream: &mut dyn Read) -> io::Result<HttpRequest> {
67 let mut reader = BufReader::new(stream);
68
69 let mut request_line = String::new();
71 reader.read_line(&mut request_line)?;
72 let request_line = request_line.trim_end();
73
74 if request_line.is_empty() {
75 return Err(io::Error::new(
76 io::ErrorKind::UnexpectedEof,
77 "Empty request",
78 ));
79 }
80
81 let parts: Vec<&str> = request_line.splitn(3, ' ').collect();
82 if parts.len() < 2 {
83 return Err(io::Error::new(
84 io::ErrorKind::InvalidData,
85 "Invalid request line",
86 ));
87 }
88
89 let method = parts[0].to_string();
90 let full_path = parts[1];
91
92 let (path, query) = if let Some(pos) = full_path.find('?') {
93 (
94 full_path[..pos].to_string(),
95 full_path[pos + 1..].to_string(),
96 )
97 } else {
98 (full_path.to_string(), String::new())
99 };
100
101 let mut headers = HashMap::new();
103 loop {
104 let mut line = String::new();
105 reader.read_line(&mut line)?;
106 let line = line.trim_end();
107 if line.is_empty() {
108 break;
109 }
110 if let Some(colon) = line.find(':') {
111 let key = line[..colon].trim().to_lowercase();
112 let value = line[colon + 1..].trim().to_string();
113 headers.insert(key, value);
114 }
115 }
116
117 let body = if let Some(len_str) = headers.get("content-length") {
119 if let Ok(len) = len_str.parse::<usize>() {
120 let mut body = vec![0u8; len];
121 reader.read_exact(&mut body)?;
122 body
123 } else {
124 Vec::new()
125 }
126 } else {
127 Vec::new()
128 };
129
130 Ok(HttpRequest {
131 method,
132 path,
133 query,
134 headers,
135 body,
136 })
137}
138
139pub fn write_response(stream: &mut dyn Write, response: &HttpResponse) -> io::Result<()> {
141 write!(
142 stream,
143 "HTTP/1.1 {} {}\r\n",
144 response.status, response.status_text
145 )?;
146 for (key, value) in &response.headers {
147 write!(stream, "{}: {}\r\n", key, value)?;
148 }
149 write!(stream, "\r\n")?;
150 stream.write_all(&response.body)?;
151 stream.flush()
152}
153
154pub fn parse_query(query: &str) -> HashMap<String, String> {
156 let mut params = HashMap::new();
157 if query.is_empty() {
158 return params;
159 }
160 for pair in query.split('&') {
161 if let Some(eq) = pair.find('=') {
162 let key = pair[..eq].to_string();
163 let value = pair[eq + 1..].to_string();
164 params.insert(key, value);
165 } else if !pair.is_empty() {
166 params.insert(pair.to_string(), String::new());
167 }
168 }
169 params
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
177 fn parse_query_basic() {
178 let q = parse_query("foo=bar&baz=123&flag");
179 assert_eq!(q.get("foo").unwrap(), "bar");
180 assert_eq!(q.get("baz").unwrap(), "123");
181 assert!(q.contains_key("flag"));
182 }
183
184 #[test]
185 fn parse_query_empty() {
186 let q = parse_query("");
187 assert!(q.is_empty());
188 }
189
190 #[test]
191 fn parse_request_get() {
192 let raw = b"GET /api/info?verbose=true HTTP/1.1\r\nHost: localhost\r\nAuthorization: Bearer abc\r\n\r\n";
193 let req = parse_request(&mut &raw[..]).unwrap();
194 assert_eq!(req.method, "GET");
195 assert_eq!(req.path, "/api/info");
196 assert_eq!(req.query, "verbose=true");
197 assert_eq!(req.headers.get("authorization").unwrap(), "Bearer abc");
198 assert!(req.body.is_empty());
199 }
200
201 #[test]
202 fn parse_request_post_with_body() {
203 let body = r#"{"key":"value"}"#;
204 let raw = format!(
205 "POST /api/send HTTP/1.1\r\nContent-Length: {}\r\n\r\n{}",
206 body.len(),
207 body
208 );
209 let req = parse_request(&mut raw.as_bytes()).unwrap();
210 assert_eq!(req.method, "POST");
211 assert_eq!(req.path, "/api/send");
212 assert_eq!(req.body, body.as_bytes());
213 }
214
215 #[test]
216 fn response_json() {
217 let resp = HttpResponse::ok(serde_json::json!({"status": "ok"}));
218 assert_eq!(resp.status, 200);
219 let body: serde_json::Value = serde_json::from_slice(&resp.body).unwrap();
220 assert_eq!(body["status"], "ok");
221 }
222}