logtail_rust/http_client/
base_client.rs1use super::{HttpClient, LogtailError};
2use reqwest::header::{HeaderMap, HeaderValue};
3use serde_json::Value;
4
5pub struct ReqwestClient;
6
7impl HttpClient for ReqwestClient {
8 async fn post_json(
9 &self,
10 url: &str,
11 body: &Value,
12 extra_headers: Option<HeaderMap>,
13 ) -> Result<Option<Value>, LogtailError> {
14 let mut header_map = HeaderMap::new();
15
16 if let Some(value) = extra_headers {
17 header_map.extend(value.iter().map(|(k, v)| (k.clone(), v.clone())))
18 }
19
20 let client = reqwest::Client::new();
21
22 let response = client
23 .post(url)
24 .headers(build_headers(Some(header_map)))
25 .json(body)
26 .send()
27 .await?;
28
29 if response.status().is_success() {
30 let body_bytes = response.bytes().await?;
31
32 if !body_bytes.is_empty() {
33 let result_value: Value = serde_json::from_slice(&body_bytes)?;
34 return Ok(Some(result_value));
35 }
36
37 Ok(None)
38 } else {
39 let status = response.status().as_u16();
40 Err(LogtailError::Http {
41 status,
42 message: format!("HTTP request failed with status {}", status),
43 })
44 }
45 }
46}
47
48fn build_headers(input_headers: Option<HeaderMap>) -> HeaderMap {
49 let mut headers = HeaderMap::new();
50 if let Some(extra) = input_headers {
51 headers = extra;
52 }
53
54 headers.insert("Accept", HeaderValue::from_static("application/json"));
55
56 headers
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[test]
64 fn build_headers_none_adds_accept() {
65 let headers = build_headers(None);
66 assert_eq!(
67 headers.get("Accept").unwrap(),
68 HeaderValue::from_static("application/json")
69 );
70 assert_eq!(headers.len(), 1);
71 }
72
73 #[test]
74 fn build_headers_merges_extra_headers() {
75 let mut extra = HeaderMap::new();
76 extra.insert("Authorization", HeaderValue::from_static("Bearer token123"));
77
78 let headers = build_headers(Some(extra));
79 assert_eq!(
80 headers.get("Authorization").unwrap(),
81 HeaderValue::from_static("Bearer token123")
82 );
83 assert_eq!(
84 headers.get("Accept").unwrap(),
85 HeaderValue::from_static("application/json")
86 );
87 assert_eq!(headers.len(), 2);
88 }
89
90 #[test]
91 fn build_headers_accept_overwrites_extra() {
92 let mut extra = HeaderMap::new();
93 extra.insert("Accept", HeaderValue::from_static("text/plain"));
94
95 let headers = build_headers(Some(extra));
96 assert_eq!(
97 headers.get("Accept").unwrap(),
98 HeaderValue::from_static("application/json")
99 );
100 }
101}