1use crate::{
4 errors::Result,
5 http::{HttpClient, HttpClientError},
6};
7use async_trait::async_trait;
8use serde_json::Value;
9
10#[async_trait]
12pub trait HttpClientWithAdminSupport: Send + Sync {
13 async fn get_value_with_admin_override(
15 &self,
16 url: &str,
17 admin_tenant: Option<&str>,
18 ) -> Result<Value>;
19}
20
21pub struct HttpClientAdapter<T: HttpClient> {
23 inner: T,
24}
25
26impl<T: HttpClient> HttpClientAdapter<T> {
27 pub fn new(client: T) -> Self {
29 Self { inner: client }
30 }
31}
32
33#[async_trait]
34impl<T: HttpClient> HttpClientWithAdminSupport for HttpClientAdapter<T> {
35 async fn get_value_with_admin_override(
36 &self,
37 url: &str,
38 admin_tenant: Option<&str>,
39 ) -> Result<Value> {
40 if admin_tenant.is_some() {
43 tracing::warn!(
44 "Admin tenant override requested but not supported by underlying client: {:?}",
45 admin_tenant
46 );
47 }
48 self.inner
49 .get_value(url)
50 .await
51 .map_err(|e| crate::errors::Error::Network(e.to_string()))
52 }
53}
54
55#[cfg(all(not(target_arch = "wasm32"), feature = "http-reqwest"))]
57#[cfg_attr(docsrs, doc(cfg(all(not(target_arch = "wasm32"), feature = "http-reqwest"))))]
58pub mod reqwest_tenant {
59 use super::*;
60 use reqwest::{Client, header::{HeaderMap, HeaderName, HeaderValue}};
61 use std::time::Duration;
62
63 #[derive(Clone)]
65 pub struct ReqwestHttpClientWithAdminSupport {
66 client: Client,
67 admin_key: Option<String>,
68 }
69
70 impl ReqwestHttpClientWithAdminSupport {
71 pub fn new() -> Result<Self> {
73 let client = Client::builder()
74 .timeout(Duration::from_secs(30))
75 .use_rustls_tls()
76 .build()
77 .map_err(|e| crate::errors::Error::Network(e.to_string()))?;
78
79 Ok(Self { client, admin_key: None })
80 }
81
82 pub fn with_admin_key(admin_key: String) -> Result<Self> {
84 let client = Client::builder()
85 .timeout(Duration::from_secs(30))
86 .use_rustls_tls()
87 .build()
88 .map_err(|e| crate::errors::Error::Network(e.to_string()))?;
89
90 Ok(Self { client, admin_key: Some(admin_key) })
91 }
92
93 pub fn with_timeout(timeout_secs: u64) -> Result<Self> {
95 let client = Client::builder()
96 .timeout(Duration::from_secs(timeout_secs))
97 .use_rustls_tls()
98 .build()
99 .map_err(|e| crate::errors::Error::Network(e.to_string()))?;
100
101 Ok(Self { client, admin_key: None })
102 }
103 }
104
105 impl Default for ReqwestHttpClientWithAdminSupport {
106 fn default() -> Self {
107 Self::new().expect("Failed to create default HTTP client")
108 }
109 }
110
111 #[async_trait]
112 impl HttpClientWithAdminSupport for ReqwestHttpClientWithAdminSupport {
113 async fn get_value_with_admin_override(
114 &self,
115 url: &str,
116 admin_tenant: Option<&str>,
117 ) -> Result<Value> {
118 let mut request = self.client.get(url);
119
120 let mut header_added = false;
122 if let (Some(admin_key), Some(tenant)) = (&self.admin_key, admin_tenant) {
123 let mut header_map = HeaderMap::new();
124
125 let admin_key_header = HeaderValue::from_str(admin_key)
127 .map_err(|e| crate::errors::Error::Network(format!("Invalid admin key: {}", e)))?;
128 header_map.insert("x-admin-key", admin_key_header);
129
130 let tenant_header = HeaderValue::from_str(tenant)
132 .map_err(|e| crate::errors::Error::Network(format!("Invalid tenant: {}", e)))?;
133 header_map.insert("x-admin-tenant", tenant_header);
134
135 request = request.headers(header_map);
136 header_added = true;
137 }
138
139 tracing::info!(
140 target: "xjp_oidc::http",
141 "HTTP 请求: {} (admin_override: {})",
142 url,
143 header_added
144 );
145
146 let response = request
147 .send()
148 .await
149 .map_err(|e| crate::errors::Error::Network(format!("Request failed: {}", e)))?;
150
151 let status = response.status();
152 let duration = std::time::Instant::now();
153
154 if !status.is_success() {
155 let error_body = response
156 .text()
157 .await
158 .unwrap_or_else(|_| "Failed to read error response".to_string());
159
160 tracing::error!(
161 target: "xjp_oidc::http",
162 "HTTP 请求失败 url={} status={} error={}",
163 url,
164 status,
165 error_body
166 );
167
168 return Err(crate::errors::Error::Network(format!(
169 "HTTP {} - {}",
170 status, error_body
171 )));
172 }
173
174 let value = response
175 .json::<Value>()
176 .await
177 .map_err(|e| crate::errors::Error::Network(format!("Failed to parse JSON: {}", e)))?;
178
179 tracing::info!(
180 target: "xjp_oidc::http",
181 "HTTP 请求完成 url={} method=\"GET\" duration_ms={} status={}",
182 url,
183 duration.elapsed().as_millis(),
184 status.as_u16()
185 );
186
187 Ok(value)
188 }
189 }
190
191 #[async_trait]
193 impl HttpClient for ReqwestHttpClientWithAdminSupport {
194 async fn get_value(&self, url: &str) -> std::result::Result<Value, HttpClientError> {
195 let response = self
196 .client
197 .get(url)
198 .send()
199 .await
200 .map_err(|e| HttpClientError::RequestFailed(e.to_string()))?;
201
202 let status = response.status();
203 if !status.is_success() {
204 let error_body = response
205 .text()
206 .await
207 .unwrap_or_else(|_| "Failed to read error response".to_string());
208
209 return Err(HttpClientError::InvalidStatus {
210 status: status.as_u16(),
211 message: error_body,
212 });
213 }
214
215 response
216 .json::<Value>()
217 .await
218 .map_err(|e| HttpClientError::ParseError(e.to_string()))
219 }
220
221 async fn post_form_value(
222 &self,
223 url: &str,
224 form: &[(String, String)],
225 auth_header: Option<(&str, &str)>,
226 ) -> std::result::Result<Value, HttpClientError> {
227 let mut request = self.client.post(url).form(form);
228
229 if let Some((key, value)) = auth_header {
230 let header_value = HeaderValue::from_str(value)
231 .map_err(|e| HttpClientError::RequestFailed(format!("Invalid header value: {}", e)))?;
232 let header_name = HeaderName::from_bytes(key.as_bytes())
233 .map_err(|e| HttpClientError::RequestFailed(format!("Invalid header name: {}", e)))?;
234 request = request.header(header_name, header_value);
235 }
236
237 let response = request
238 .send()
239 .await
240 .map_err(|e| HttpClientError::RequestFailed(e.to_string()))?;
241
242 let status = response.status();
243 if !status.is_success() {
244 let error_body = response
245 .text()
246 .await
247 .unwrap_or_else(|_| "Failed to read error response".to_string());
248
249 return Err(HttpClientError::InvalidStatus {
250 status: status.as_u16(),
251 message: error_body,
252 });
253 }
254
255 response
256 .json::<Value>()
257 .await
258 .map_err(|e| HttpClientError::ParseError(e.to_string()))
259 }
260
261 async fn post_json_value(
262 &self,
263 url: &str,
264 body: &Value,
265 auth_header: Option<(&str, &str)>,
266 ) -> std::result::Result<Value, HttpClientError> {
267 let mut request = self.client.post(url).json(body);
268
269 if let Some((key, value)) = auth_header {
270 let header_value = HeaderValue::from_str(value)
271 .map_err(|e| HttpClientError::RequestFailed(format!("Invalid header value: {}", e)))?;
272 let header_name = HeaderName::from_bytes(key.as_bytes())
273 .map_err(|e| HttpClientError::RequestFailed(format!("Invalid header name: {}", e)))?;
274 request = request.header(header_name, header_value);
275 }
276
277 let response = request
278 .send()
279 .await
280 .map_err(|e| HttpClientError::RequestFailed(e.to_string()))?;
281
282 let status = response.status();
283 if !status.is_success() {
284 let error_body = response
285 .text()
286 .await
287 .unwrap_or_else(|_| "Failed to read error response".to_string());
288
289 return Err(HttpClientError::InvalidStatus {
290 status: status.as_u16(),
291 message: error_body,
292 });
293 }
294
295 response
296 .json::<Value>()
297 .await
298 .map_err(|e| HttpClientError::ParseError(e.to_string()))
299 }
300 }
301}