rust_integration_services/http/
http_response.rs1use std::{collections::HashMap};
2
3use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncReadExt, BufReader};
4
5#[allow(dead_code)]
6#[derive(Debug, Clone)]
7pub struct HttpResponse {
8 pub protocol: String,
9 pub status_code: u16,
10 pub status_text: String,
11 pub headers: HashMap<String, String>,
12 pub body: Vec<u8>,
13}
14
15#[allow(dead_code)]
16impl HttpResponse {
17 pub fn new() -> Self {
18 HttpResponse {
19 protocol: String::from("HTTP/1.1"),
20 status_code: 0,
21 status_text: String::new(),
22 headers: HashMap::new(),
23 body: Vec::new(),
24 }
25 }
26
27 pub fn ok() -> Self {
28 HttpResponse::new().status(200, "OK")
29 }
30
31 pub fn found() -> Self {
32 HttpResponse::new().status(302, "Found")
33 }
34
35 pub fn bad_request() -> Self {
36 HttpResponse::new().status(400, "Bad Request")
37 }
38
39 pub fn unauthorized() -> Self {
40 HttpResponse::new().status(401, "Unauthorized")
41 }
42
43 pub fn forbidden() -> Self {
44 HttpResponse::new().status(403, "Forbidden")
45 }
46
47 pub fn not_found() -> Self {
48 HttpResponse::new().status(404, "Not Found")
49 }
50
51 pub fn internal_server_error() -> Self {
52 HttpResponse::new().status(500, "Internal Server Error")
53 }
54
55 pub fn status(mut self, code: u16, text: &str) -> Self {
56 self.status_code = code;
57 self.status_text = text.to_string();
58 self
59 }
60
61 pub fn body_bytes(mut self, body: &[u8]) -> Self {
62 self.body = body.to_vec();
63 self.headers.insert(String::from("Content-Length"), String::from(body.len().to_string()));
64 self
65 }
66
67 pub fn body_string(mut self, body: &str) -> Self {
68 self.body = body.as_bytes().to_vec();
69 self.headers.insert(String::from("Content-Length"), String::from(body.len().to_string()));
70 self
71 }
72
73 pub fn header(mut self, key: &str, value: &str) -> Self {
74 self.headers.insert(key.to_string(), value.to_string());
75 self
76 }
77
78 pub fn body_to_string(&self) -> String {
79 String::from_utf8_lossy(&self.body).to_string()
80 }
81
82 pub fn to_bytes(&self) -> Vec<u8> {
83 let mut bytes: Vec<u8> = Vec::new();
84
85 let first_line_str = format!("{} {} {}", self.protocol, self.status_code, self.status_text);
86
87 let mut headers_str = String::new();
88 for (key, value) in &self.headers {
89 headers_str.push_str(&format!("{}: {}\r\n", key, value));
90 };
91
92 bytes.extend_from_slice(first_line_str.as_bytes());
93 bytes.extend_from_slice("\r\n".as_bytes());
94 bytes.extend_from_slice(headers_str.as_bytes());
95 bytes.extend_from_slice("\r\n".as_bytes());
96 bytes.extend(self.body.clone());
97 bytes
98 }
99
100 pub async fn from_stream<S: AsyncRead + Unpin>(stream: &mut S) -> tokio::io::Result<HttpResponse> {
101 let mut reader = BufReader::new(stream);
102 let mut buffer = String::new();
103
104 reader.read_line(&mut buffer).await?;
105 let first_line: Vec<&str> = buffer.split_whitespace().collect();
106 let protocol = first_line[0].to_string();
107 let status_code = first_line[1].to_string().parse().unwrap();
108 let status_text = first_line[2].to_string();
109
110 let mut header = String::new();
111 let mut headers: HashMap<String, String> = HashMap::new();
112 loop {
113 header.clear();
114 reader.read_line(&mut header).await?;
115
116 if header.trim().is_empty() {
117 break;
118 }
119
120 if let Some((key, value)) = header.trim().split_once(":") {
121 headers.insert(key.trim().to_string(), value.trim().to_string());
122 }
123 }
124
125 let mut body = Vec::new();
126 if let Some(content_length) = headers.get("Content-Length") {
127 let length = content_length.parse().unwrap_or(0);
128 let mut bytes = vec![0; length];
129 reader.read_exact(&mut bytes).await?;
130 body = bytes;
131 }
132
133 Ok(HttpResponse {
134 protocol,
135 status_code,
136 status_text,
137 headers,
138 body
139 })
140 }
141}