stackforge_core/layer/http/
builder.rs1#[derive(Debug, Clone)]
45pub struct HttpRequestBuilder {
46 method: String,
47 uri: String,
48 version: String,
49 headers: Vec<(String, String)>,
50 body: Vec<u8>,
51}
52
53impl Default for HttpRequestBuilder {
54 fn default() -> Self {
55 Self {
56 method: "GET".to_owned(),
57 uri: "/".to_owned(),
58 version: "HTTP/1.1".to_owned(),
59 headers: Vec::new(),
60 body: Vec::new(),
61 }
62 }
63}
64
65impl HttpRequestBuilder {
66 pub fn new() -> Self {
68 Self::default()
69 }
70
71 pub fn method(mut self, method: &str) -> Self {
73 self.method = method.to_owned();
74 self
75 }
76
77 pub fn uri(mut self, uri: &str) -> Self {
79 self.uri = uri.to_owned();
80 self
81 }
82
83 pub fn version(mut self, version: &str) -> Self {
85 self.version = version.to_owned();
86 self
87 }
88
89 pub fn header(mut self, name: &str, value: &str) -> Self {
94 self.headers.push((name.to_owned(), value.to_owned()));
95 self
96 }
97
98 pub fn body(mut self, body: Vec<u8>) -> Self {
103 self.body = body;
104 self
105 }
106
107 pub fn build(&self) -> Vec<u8> {
120 let mut out = Vec::new();
121
122 out.extend_from_slice(self.method.as_bytes());
124 out.push(b' ');
125 out.extend_from_slice(self.uri.as_bytes());
126 out.push(b' ');
127 out.extend_from_slice(self.version.as_bytes());
128 out.extend_from_slice(b"\r\n");
129
130 for (name, value) in &self.headers {
132 out.extend_from_slice(name.as_bytes());
133 out.extend_from_slice(b": ");
134 out.extend_from_slice(value.as_bytes());
135 out.extend_from_slice(b"\r\n");
136 }
137
138 out.extend_from_slice(b"\r\n");
140
141 out.extend_from_slice(&self.body);
143
144 out
145 }
146}
147
148#[derive(Debug, Clone)]
161pub struct HttpResponseBuilder {
162 version: String,
163 status_code: u16,
164 reason: String,
165 headers: Vec<(String, String)>,
166 body: Vec<u8>,
167}
168
169impl Default for HttpResponseBuilder {
170 fn default() -> Self {
171 Self {
172 version: "HTTP/1.1".to_owned(),
173 status_code: 200,
174 reason: "OK".to_owned(),
175 headers: Vec::new(),
176 body: Vec::new(),
177 }
178 }
179}
180
181impl HttpResponseBuilder {
182 pub fn new() -> Self {
184 Self::default()
185 }
186
187 pub fn version(mut self, version: &str) -> Self {
189 self.version = version.to_owned();
190 self
191 }
192
193 pub fn status(mut self, code: u16, reason: &str) -> Self {
207 self.status_code = code;
208 self.reason = reason.to_owned();
209 self
210 }
211
212 pub fn header(mut self, name: &str, value: &str) -> Self {
217 self.headers.push((name.to_owned(), value.to_owned()));
218 self
219 }
220
221 pub fn body(mut self, body: Vec<u8>) -> Self {
226 self.body = body;
227 self
228 }
229
230 pub fn build(&self) -> Vec<u8> {
243 let mut out = Vec::new();
244
245 out.extend_from_slice(self.version.as_bytes());
247 out.push(b' ');
248 out.extend_from_slice(self.status_code.to_string().as_bytes());
249 out.push(b' ');
250 out.extend_from_slice(self.reason.as_bytes());
251 out.extend_from_slice(b"\r\n");
252
253 for (name, value) in &self.headers {
255 out.extend_from_slice(name.as_bytes());
256 out.extend_from_slice(b": ");
257 out.extend_from_slice(value.as_bytes());
258 out.extend_from_slice(b"\r\n");
259 }
260
261 out.extend_from_slice(b"\r\n");
263
264 out.extend_from_slice(&self.body);
266
267 out
268 }
269}
270
271#[cfg(test)]
276mod tests {
277 use super::*;
278 use crate::layer::http::request::HttpRequest;
279 use crate::layer::http::response::HttpResponse;
280
281 #[test]
282 fn test_request_builder_defaults() {
283 let bytes = HttpRequestBuilder::new().build();
284 assert!(bytes.starts_with(b"GET / HTTP/1.1\r\n"));
285 assert!(bytes.ends_with(b"\r\n\r\n"));
286 }
287
288 #[test]
289 fn test_request_builder_full() {
290 let body = b"key=value".to_vec();
291 let bytes = HttpRequestBuilder::new()
292 .method("POST")
293 .uri("/submit")
294 .version("HTTP/1.1")
295 .header("Host", "example.com")
296 .header("Content-Type", "application/x-www-form-urlencoded")
297 .header("Content-Length", &body.len().to_string())
298 .body(body.clone())
299 .build();
300
301 let req = HttpRequest::parse(&bytes).expect("should parse");
303 assert_eq!(req.method, "POST");
304 assert_eq!(req.uri, "/submit");
305 assert_eq!(req.version, "HTTP/1.1");
306 assert_eq!(req.headers.len(), 3);
307 let parsed_body = &bytes[req.body_offset..];
308 assert_eq!(parsed_body, body.as_slice());
309 }
310
311 #[test]
312 fn test_response_builder_defaults() {
313 let bytes = HttpResponseBuilder::new().build();
314 assert!(bytes.starts_with(b"HTTP/1.1 200 OK\r\n"));
315 assert!(bytes.ends_with(b"\r\n\r\n"));
316 }
317
318 #[test]
319 fn test_response_builder_full() {
320 let body = b"Hello, World!".to_vec();
321 let bytes = HttpResponseBuilder::new()
322 .status(200, "OK")
323 .header("Content-Type", "text/plain")
324 .header("Content-Length", &body.len().to_string())
325 .body(body.clone())
326 .build();
327
328 let resp = HttpResponse::parse(&bytes).expect("should parse");
330 assert_eq!(resp.status_code, 200);
331 assert_eq!(resp.reason, "OK");
332 assert_eq!(resp.headers.len(), 2);
333 let parsed_body = &bytes[resp.body_offset..];
334 assert_eq!(parsed_body, body.as_slice());
335 }
336
337 #[test]
338 fn test_response_builder_404() {
339 let bytes = HttpResponseBuilder::new().status(404, "Not Found").build();
340
341 assert!(bytes.starts_with(b"HTTP/1.1 404 Not Found\r\n"));
342 let resp = HttpResponse::parse(&bytes).unwrap();
343 assert_eq!(resp.status_code, 404);
344 assert_eq!(resp.reason, "Not Found");
345 }
346
347 #[test]
348 fn test_request_builder_http10() {
349 let bytes = HttpRequestBuilder::new()
350 .version("HTTP/1.0")
351 .uri("/old")
352 .build();
353 assert!(bytes.starts_with(b"GET /old HTTP/1.0\r\n"));
354 }
355
356 #[test]
357 fn test_multiple_headers_ordering() {
358 let bytes = HttpRequestBuilder::new()
359 .header("A", "1")
360 .header("B", "2")
361 .header("C", "3")
362 .build();
363
364 let req = HttpRequest::parse(&bytes).unwrap();
365 assert_eq!(req.headers[0], ("A", "1"));
366 assert_eq!(req.headers[1], ("B", "2"));
367 assert_eq!(req.headers[2], ("C", "3"));
368 }
369}