1use std::collections::HashMap;
2use std::error::Error;
3use std::io::{Read, Write};
4use std::net::{TcpStream, ToSocketAddrs};
5use std::time::Duration;
6use std::fmt::Write as FmtWrite;
7
8struct HttpClient {
9 host: String,
10 port: u16,
11 path: String,
12 timeout: Duration,
13}
14
15const DEFAULT_TIMEOUT: Duration = Duration::from_secs(5);
16
17impl HttpClient {
18 fn new(host: &str, port: u16, path: &str, timeout: Duration) -> Self {
19 HttpClient {
20 host: host.to_string(),
21 port,
22 path: path.to_string(),
23 timeout,
24 }
25 }
26
27 fn request(
28 &self,
29 method: &str,
30 headers: Option<HashMap<&str, &str>>,
31 body: Option<&str>,
32 ) -> Result<String, Box<dyn Error>> {
33 let addr = format!("{}:{}", self.host, self.port);
34 let mut addrs = addr.to_socket_addrs()?;
35 let socket_addr = addrs.next().ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "Could not resolve address"))?;
36
37 let mut stream = TcpStream::connect_timeout(&socket_addr, Duration::from_secs(2))?;
38
39 stream.set_read_timeout(Some(self.timeout))?;
40 stream.set_write_timeout(Some(self.timeout))?;
41
42 let mut request = String::new();
43 write!(&mut request, "{} {} HTTP/1.1\r\nHost: {}\r\n", method, self.path, self.host)?;
44 write!(&mut request, "User-Agent: Rust HTTP Client\r\n")?;
45 if let Some(headers) = headers {
46 for (key, value) in headers {
47 write!(&mut request, "{}: {}\r\n", key, value)?;
48 }
49 }
50 write!(request, "Connection: close\r\n")?;
51 if let Some(body) = body {
52 write!(&mut request, "Content-Length: {}\r\n", body.len())?;
53 }
54
55 write!(&mut request, "\r\n")?;
56 if let Some(body) = body {
57 write!(&mut request, "{}", body)?;
58 }
59
60 stream.write_all(request.as_bytes())?;
61
62 let mut response = String::new();
63 stream.read_to_string(&mut response)?;
64
65 let body = response.split("\r\n\r\n").nth(1).unwrap_or("");
66 Ok(body.to_string())
67 }
68}
69
70fn client(url: &str, timeout: Duration) -> Result<HttpClient, Box<dyn Error>> {
71 let url = url.strip_prefix("http://")
73 .or_else(|| url.strip_prefix("https://"))
74 .ok_or("Invalid URL: Missing protocol (http or https)")?;
75
76 let mut parts = url.splitn(2, '/');
78 let host_and_port = parts.next().ok_or("Invalid URL: Missing host")?;
79 let path = format!("/{}", parts.next().unwrap_or(""));
80
81 let mut host_parts = host_and_port.splitn(2, ':');
83 let host = host_parts.next().ok_or("Invalid URL: Missing host")?.to_string();
84 let port = host_parts
85 .next()
86 .map(|p| p.parse::<u16>().map_err(|_| "Invalid port"))
87 .transpose()?
88 .unwrap_or(80);
89
90 Ok(HttpClient::new(&host, port, &path, timeout))
91}
92
93pub fn get(url: &str, headers: Option<HashMap<&str, &str>>) -> Result<String, Box<dyn Error>> {
102 let client = client(url, DEFAULT_TIMEOUT)?;
103 client.request("GET", headers, None)
104}
105
106pub fn post(url: &str, headers: Option<HashMap<&str, &str>>, body: Option<&str>) -> Result<String, Box<dyn Error>> {
115 let client = client(url, DEFAULT_TIMEOUT)?;
116 client.request("POST", headers, body)
117}
118
119pub fn put(url: &str, headers: Option<HashMap<&str, &str>>, body: Option<&str>) -> Result<String, Box<dyn Error>> {
129 let client = client(url, DEFAULT_TIMEOUT)?;
130 client.request("PUT", headers, body)
131}
132
133pub fn delete(url: &str, headers: Option<HashMap<&str, &str>>) -> Result<String, Box<dyn Error>> {
142 let client = client(url, DEFAULT_TIMEOUT)?;
143 client.request("DELETE", headers, None)
144}
145
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn test_get() {
153 let response = get("http://example.com", None).expect("GET request failed");
154 println!("Response: {}", response);
157 }
158
159 #[test]
160 fn test_post() {
161 let response = post("http://192.168.111.250:9610/api/v1/index/system/state/get", None, None).
162 expect("POST request failed");
163 println!("Response: {}", response);
165 }
166
167 #[test]
168 fn test_put() {
169 let response = put("http://192.168.110.106:9900/api/v1/sys/node/dtu", None,
170 Some("{\"dtu\": true, \"identity\": \"e540f857-704b-4985-bb69-3d6c935debb0\"}")).
171 expect("PUT request failed");
172 println!("Response: {}", response);
174 }
175
176 #[test]
177 fn test_delete() {
178 let response = delete("http://192.168.110.106:9900/api/v1/sys/param/quality/delete?identity=1", None).
179 expect("DELETE request failed");
180 println!("Response: {}", response);
182 }
183}