1use crate::error::{Result, TurboCdnError};
10use std::collections::HashMap;
11use std::time::Duration;
12
13#[derive(Debug)]
15pub struct HttpResponse {
16 pub status: u16,
17 pub headers: HashMap<String, String>,
18 pub body: Vec<u8>,
19}
20
21#[derive(Debug)]
23pub struct HttpClient {
24 client: reqwest::Client,
25}
26
27impl HttpClient {
28 pub fn new(timeout: Duration) -> Result<Self> {
30 crate::init_rustls_provider();
32
33 let client = reqwest::Client::builder()
34 .timeout(timeout)
35 .tcp_keepalive(Duration::from_secs(60))
36 .tcp_nodelay(true)
37 .pool_max_idle_per_host(20)
38 .http2_prior_knowledge()
39 .build()
40 .map_err(|e| TurboCdnError::network(format!("Failed to create HTTP client: {e}")))?;
41
42 Ok(Self { client })
43 }
44
45 pub async fn get(&self, url: &str) -> Result<HttpResponse> {
47 let response = self
48 .client
49 .get(url)
50 .send()
51 .await
52 .map_err(|e| TurboCdnError::network(format!("GET request failed: {e}")))?;
53
54 let status = response.status().as_u16();
55 let headers = response
56 .headers()
57 .iter()
58 .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string()))
59 .collect();
60
61 let body = response
62 .bytes()
63 .await
64 .map_err(|e| TurboCdnError::network(format!("Failed to read response body: {e}")))?
65 .to_vec();
66
67 Ok(HttpResponse {
68 status,
69 headers,
70 body,
71 })
72 }
73
74 pub async fn get_with_headers(
76 &self,
77 url: &str,
78 request_headers: &HashMap<String, String>,
79 ) -> Result<HttpResponse> {
80 let mut request = self.client.get(url);
81
82 for (key, value) in request_headers {
83 request = request.header(key, value);
84 }
85
86 let response = request
87 .send()
88 .await
89 .map_err(|e| TurboCdnError::network(format!("GET request with headers failed: {e}")))?;
90
91 let status = response.status().as_u16();
92 let headers = response
93 .headers()
94 .iter()
95 .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string()))
96 .collect();
97
98 let body = response
99 .bytes()
100 .await
101 .map_err(|e| TurboCdnError::network(format!("Failed to read response body: {e}")))?
102 .to_vec();
103
104 Ok(HttpResponse {
105 status,
106 headers,
107 body,
108 })
109 }
110
111 pub async fn head(&self, url: &str) -> Result<HttpResponse> {
113 let response = self
114 .client
115 .head(url)
116 .send()
117 .await
118 .map_err(|e| TurboCdnError::network(format!("HEAD request failed: {e}")))?;
119
120 let status = response.status().as_u16();
121 let headers = response
122 .headers()
123 .iter()
124 .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string()))
125 .collect();
126
127 Ok(HttpResponse {
128 status,
129 headers,
130 body: Vec::new(),
131 })
132 }
133
134 pub fn name(&self) -> &'static str {
136 "reqwest"
137 }
138}