rust_integration_services/http/
http_request.rs1use std::{collections::HashMap};
2
3use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncWrite, BufReader};
4
5#[allow(dead_code)]
6#[derive(Debug, Clone)]
7pub struct HttpRequest {
8 pub method: String,
9 pub path: String,
10 pub protocol: String,
11 pub headers: HashMap<String, String>,
12 pub body: Vec<u8>,
13 pub ip: Option<String>,
14}
15
16#[allow(dead_code)]
17impl HttpRequest {
18 pub fn new() -> Self {
19 HttpRequest {
20 method: String::new(),
21 path: String::new(),
22 protocol: String::from("HTTP/1.1"),
23 headers: HashMap::new(),
24 body: Vec::new(),
25 ip: None,
26 }
27 }
28
29 pub fn get() -> Self {
33 HttpRequest::new().method("GET").path("/")
34 }
35
36 pub fn post() -> Self {
40 HttpRequest::new().method("POST").path("/")
41 }
42
43 pub fn put() -> Self {
47 HttpRequest::new().method("PUT").path("/")
48 }
49
50 pub fn delete() -> Self {
54 HttpRequest::new().method("DELETE").path("/")
55 }
56
57 pub fn header(mut self, key: &str, value: &str) -> Self {
58 self.headers.insert(key.to_string(), value.to_string());
59 self
60 }
61
62 pub fn body_bytes(mut self, body: &[u8]) -> Self {
63 self.body = body.to_vec();
64 self.headers.insert(String::from("Content-Length"), String::from(body.len().to_string()));
65 self
66 }
67
68 pub fn body_string(mut self, body: &str) -> Self {
69 self.body = body.as_bytes().to_vec();
70 self.headers.insert(String::from("Content-Length"), String::from(body.len().to_string()));
71 self
72 }
73
74 pub fn method(mut self, method: &str) -> Self {
75 self.method = method.to_uppercase();
76 self
77 }
78
79 pub fn path(mut self, path: &str) -> Self {
80 self.path = path.to_string();
81 self
82 }
83
84 pub fn ip(mut self, ip: String) -> Self {
85 self.ip = Some(ip);
86 self
87 }
88
89 pub fn body_to_string(&self) -> String {
90 String::from_utf8_lossy(&self.body).to_string()
91 }
92
93 pub fn to_bytes(&self) -> Vec<u8> {
94 let mut bytes: Vec<u8> = Vec::new();
95 let first_line_str = format!("{} {} {}", self.method, self.path, self.protocol);
96
97 let mut headers_str = String::new();
98 for (key, value) in &self.headers {
99 headers_str.push_str(&format!("{}: {}\r\n", key, value));
100 };
101
102 bytes.extend_from_slice(first_line_str.as_bytes());
103 bytes.extend_from_slice("\r\n".as_bytes());
104 bytes.extend_from_slice(headers_str.as_bytes());
105 bytes.extend_from_slice("\r\n".as_bytes());
106 bytes.extend(self.body.clone());
107 bytes
108 }
109
110 pub async fn from_stream<S: AsyncRead + AsyncWrite + Unpin>(stream: &mut S) -> tokio::io::Result<HttpRequest> {
111 let mut reader = BufReader::new(stream);
112 let mut buffer = String::new();
113
114 reader.read_line(&mut buffer).await?;
115 let first_line: Vec<&str> = buffer.split_whitespace().collect();
116 let method = first_line[0];
117 let path = first_line[1];
118 let protocol = first_line[2];
119
120 let mut header = String::new();
121 let mut headers: HashMap<String, String> = HashMap::new();
122 loop {
123 header.clear();
124 reader.read_line(&mut header).await?;
125
126 if header.trim().is_empty() {
127 break;
128 }
129
130 if let Some((key, value)) = header.trim().split_once(":") {
131 headers.insert(key.trim().to_string(), value.trim().to_string());
132 }
133 }
134
135 let mut body = Vec::new();
136 if let Some(content_length) = headers.get("Content-Length") {
137 let length = content_length.parse().unwrap_or(0);
138 let mut bytes = vec![0; length];
139 reader.read_exact(&mut bytes).await?;
140 body = bytes;
141 }
142
143 Ok(HttpRequest {
144 method: String::from(method),
145 path: String::from(path),
146 protocol: String::from(protocol),
147 headers: headers,
148 body: body,
149 ip: None,
150 })
151 }
152}