1use serde_json::{json, Value as JsonValue};
2use std::time::Duration;
3
4pub fn request(method: &str, url: &str, body: Option<&JsonValue>) -> JsonValue {
5 let request = HttpRequest::from_parts(method, url, body.cloned());
6 execute(&request).to_json()
7}
8
9#[derive(Debug, Clone)]
10struct HttpRequest {
11 method: String,
12 url: String,
13 body: Option<JsonValue>,
14}
15
16impl HttpRequest {
17 fn from_parts(method: &str, url: &str, body: Option<JsonValue>) -> Self {
18 Self {
19 method: method.to_string(),
20 url: url.to_string(),
21 body,
22 }
23 }
24}
25
26#[derive(Debug, Clone)]
27struct HttpResponse {
28 method: String,
29 url: String,
30 status: Option<u16>,
31 status_line: Option<String>,
32 body: Option<String>,
33 error: Option<String>,
34}
35
36impl HttpResponse {
37 fn error(method: &str, url: &str, error: String) -> Self {
38 Self {
39 method: method.to_string(),
40 url: url.to_string(),
41 status: None,
42 status_line: None,
43 body: None,
44 error: Some(error),
45 }
46 }
47
48 fn to_json(&self) -> JsonValue {
49 if let Some(error) = &self.error {
50 return json!({
51 "error": error,
52 "method": self.method,
53 "url": self.url,
54 });
55 }
56
57 json!({
58 "method": self.method,
59 "url": self.url,
60 "status": self.status,
61 "status_line": self.status_line,
62 "body": self.body,
63 })
64 }
65}
66
67fn execute(request: &HttpRequest) -> HttpResponse {
68 if !request.url.starts_with("http://") && !request.url.starts_with("https://") {
69 return HttpResponse::error(
70 &request.method,
71 &request.url,
72 "host http adapter supports only http:// and https:// URLs".to_string(),
73 );
74 }
75
76 let payload = request
77 .body
78 .as_ref()
79 .map(|b| serde_json::to_vec(b).unwrap_or_else(|_| b"null".to_vec()))
80 .unwrap_or_default();
81
82 let mut ehttp_request = if request.method == "POST" {
83 let mut req = ehttp::Request::post(&request.url, payload);
84 req.headers.insert("Content-Type", "application/json");
85 req
86 } else {
87 ehttp::Request::get(&request.url)
88 };
89 ehttp_request.timeout = Some(Duration::from_secs(15));
90
91 let response = match ehttp::fetch_blocking(&ehttp_request) {
92 Ok(resp) => resp,
93 Err(err) => {
94 return HttpResponse::error(
95 &request.method,
96 &request.url,
97 format!("request failed: {err}"),
98 );
99 }
100 };
101
102 let response_body = String::from_utf8_lossy(&response.bytes).to_string();
103 let status_line = format!("HTTP {} {}", response.status, response.status_text);
104
105 HttpResponse {
106 method: request.method.clone(),
107 url: request.url.clone(),
108 status: Some(response.status),
109 status_line: Some(status_line),
110 body: Some(response_body),
111 error: None,
112 }
113}