1use std::sync::Arc;
7use std::time::Duration;
8
9use reqwest::{Client, Method};
10use serde::{de::DeserializeOwned, Serialize};
11use serde_json::Value;
12
13use crate::error::{SdkError, SdkResult};
14use crate::protocols::auth::{ApiError, ErrorDetail};
15use crate::token_manager::TokenManager;
16
17fn generate_curl_command(
19 method: &Method,
20 url: &str,
21 headers: &[(&str, &str)],
22 body: Option<&str>,
23) -> String {
24 let mut curl = format!("curl -X {} '{}'{}", method.as_str(), url, " \\\n ");
25
26 for (key, value) in headers {
28 curl.push_str(&format!("-H '{}: {}'{}", key, value, " \\\n "));
29 }
30
31 if let Some(body_str) = body {
33 if let Ok(json_value) = serde_json::from_str::<Value>(body_str) {
35 if let Ok(pretty_json) = serde_json::to_string(&json_value) {
36 curl.push_str(&format!("-d '{}'", pretty_json));
37 } else {
38 curl.push_str(&format!("-d '{}'", body_str));
39 }
40 } else {
41 curl.push_str(&format!("-d '{}'", body_str));
42 }
43 }
44
45 curl
46}
47
48#[derive(Clone)]
50pub struct HttpClient {
51 client: Client,
52 base_url: String,
53 token_manager: Arc<TokenManager>,
54}
55
56impl HttpClient {
57 pub fn new(base_url: &str) -> Self {
59 let client = Client::builder()
60 .timeout(Duration::from_secs(30))
61 .connect_timeout(Duration::from_secs(10))
62 .http1_only()
63 .user_agent("AgentLink-SDK/1.0")
64 .no_proxy()
65 .build()
66 .expect("Failed to create HTTP client");
67
68 Self {
69 client,
70 base_url: base_url.to_string(),
71 token_manager: Arc::new(TokenManager::new()),
72 }
73 }
74
75 pub fn with_token_manager(base_url: &str, token_manager: Arc<TokenManager>) -> Self {
77 let client = Client::builder()
78 .timeout(Duration::from_secs(30))
79 .connect_timeout(Duration::from_secs(10))
80 .http1_only()
81 .user_agent("AgentLink-SDK/1.0")
82 .no_proxy()
83 .build()
84 .expect("Failed to create HTTP client");
85
86 Self {
87 client,
88 base_url: base_url.to_string(),
89 token_manager,
90 }
91 }
92
93 pub fn token_manager(&self) -> &TokenManager {
95 &self.token_manager
96 }
97
98 pub async fn get<T: DeserializeOwned>(&self, path: &str) -> SdkResult<T> {
100 self.request::<T, ()>(Method::GET, path, None).await
101 }
102
103 pub async fn post<T: DeserializeOwned, B: Serialize + Sync>(&self, path: &str, body: &B) -> SdkResult<T> {
105 self.request(Method::POST, path, Some(body)).await
106 }
107
108 pub async fn put<T: DeserializeOwned, B: Serialize + Sync>(&self, path: &str, body: &B) -> SdkResult<T> {
110 self.request(Method::PUT, path, Some(body)).await
111 }
112
113 pub async fn delete<T: DeserializeOwned>(&self, path: &str) -> SdkResult<T> {
115 self.request::<T, ()>(Method::DELETE, path, None).await
116 }
117
118 async fn request<T: DeserializeOwned, B: Serialize + Sync>(
120 &self,
121 method: Method,
122 path: &str,
123 body: Option<&B>,
124 ) -> SdkResult<T> {
125 let url = format!("{}{}", self.base_url, path);
126
127 let mut request = self.client.request(method.clone(), &url);
128 request = request.header("Accept", "application/json");
129
130 let mut headers_for_curl: Vec<(&str, String)> = vec![
132 ("Accept", "application/json".to_string()),
133 ];
134
135 if let Some(token) = self.token_manager.get_token() {
136 let auth_header = format!("Bearer {}", token);
137 request = request.header("Authorization", &auth_header);
138 headers_for_curl.push(("Authorization", auth_header));
139 }
140
141 let body_str = body.map(|b| serde_json::to_string(b).unwrap_or_default());
143
144 if let Some(ref b) = body_str {
145 request = request.header("Content-Type", "application/json");
146 request = request.body(b.clone());
147 headers_for_curl.push(("Content-Type", "application/json".to_string()));
148 }
149
150 let headers_ref: Vec<(&str, &str)> = headers_for_curl
152 .iter()
153 .map(|(k, v)| (*k, v.as_str()))
154 .collect();
155 let curl_cmd = generate_curl_command(
156 &method,
157 &url,
158 &headers_ref,
159 body_str.as_deref(),
160 );
161 tracing::info!("[HTTP] curl command:\n{}", curl_cmd);
162
163 let response = request.send().await.map_err(SdkError::from)?;
164 let status = response.status();
165 let status_code = status.as_u16();
166 let response_text = response.text().await.map_err(SdkError::from)?;
167
168 self.handle_response(response_text, status_code).await
169 }
170
171 async fn handle_response<T: DeserializeOwned>(
173 &self,
174 response_text: String,
175 status_code: u16,
176 ) -> SdkResult<T> {
177 if status_code >= 400 {
179 if let Ok(json_value) = serde_json::from_str::<Value>(&response_text) {
181 if let Some(success) = json_value.get("success").and_then(|v| v.as_bool()) {
182 if !success {
183 if let Some(error) = json_value.get("error") {
184 if let Ok(error_detail) = serde_json::from_value::<ErrorDetail>(error.clone()) {
185 return Err(SdkError::Http(format!(
186 "{}: {}",
187 error_detail.code, error_detail.message
188 )));
189 }
190 }
191 }
192 }
193
194 if let Ok(api_error) = serde_json::from_str::<ApiError>(&response_text) {
196 let error_msg = api_error.error.to_lowercase();
197 let error_code = if error_msg.contains("invalid token") || error_msg.contains("token 无效") {
198 "INVALID_TOKEN"
199 } else if error_msg.contains("token expired") || error_msg.contains("token 已过期") {
200 "TOKEN_EXPIRED"
201 } else if error_msg.contains("user not found") || error_msg.contains("用户不存在") {
202 "USER_NOT_FOUND"
203 } else if status_code == 401 {
204 "UNAUTHORIZED"
205 } else if status_code == 403 {
206 "FORBIDDEN"
207 } else if status_code == 404 {
208 "NOT_FOUND"
209 } else if status_code >= 500 {
210 "SERVER_ERROR"
211 } else {
212 "API_ERROR"
213 };
214
215 return Err(SdkError::Http(format!("{}: {}", error_code, api_error.error)));
216 }
217 }
218
219 return Err(SdkError::Http(format!(
220 "HTTP {}: {}",
221 status_code, response_text
222 )));
223 }
224
225 let json_value: Value = serde_json::from_str(&response_text).map_err(SdkError::from)?;
227
228 if let Some(success) = json_value.get("success").and_then(|v| v.as_bool()) {
229 if success {
230 if let Some(data) = json_value.get("data") {
231 let parsed: T = serde_json::from_value(data.clone())
232 .map_err(|e| SdkError::Serialization(e.to_string()))?;
233 return Ok(parsed);
234 }
235 } else {
236 if let Some(error) = json_value.get("error") {
237 if let Ok(error_detail) = serde_json::from_value::<ErrorDetail>(error.clone()) {
238 return Err(SdkError::Http(format!(
239 "{}: {}",
240 error_detail.code, error_detail.message
241 )));
242 }
243 }
244 }
245 }
246
247 match serde_json::from_str::<T>(&response_text) {
249 Ok(data) => Ok(data),
250 Err(e) => Err(SdkError::Serialization(e.to_string())),
251 }
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258
259 #[test]
260 fn test_http_client_new() {
261 let client = HttpClient::new("http://localhost:8080/api/v1");
262 assert_eq!(client.base_url, "http://localhost:8080/api/v1");
263 }
264
265 #[test]
266 fn test_http_client_clone() {
267 let client1 = HttpClient::new("http://localhost:8080/api/v1");
268 let client2 = client1.clone();
269 assert_eq!(client1.base_url, client2.base_url);
270 }
271}