1use crate::auth::AuthManager;
4use crate::config::HttpConfig;
5use crate::error::HttpError;
6use crate::model::response::api_response::ApiResponse;
7use crate::model::types::AuthToken;
8use crate::rate_limit::{RateLimiter, categorize_endpoint};
9use crate::sync_compat::Mutex;
10use reqwest::Client;
11use serde::de::DeserializeOwned;
12use std::sync::Arc;
13
14#[derive(Debug, Clone)]
16pub struct DeribitHttpClient {
17 client: Client,
19 config: Arc<HttpConfig>,
21 rate_limiter: RateLimiter,
23 auth_manager: Arc<Mutex<AuthManager>>,
25}
26
27impl DeribitHttpClient {
28 pub fn new() -> Self {
30 let config = HttpConfig::default();
31 Self::with_config(config)
32 }
33
34 pub fn with_config(config: HttpConfig) -> Self {
36 let builder = Client::builder();
37
38 #[cfg(not(target_arch = "wasm32"))]
39 let builder = builder
40 .timeout(config.timeout)
41 .user_agent(&config.user_agent);
42
43 let client = builder.build().expect("Failed to create HTTP client");
44
45 let auth_manager = AuthManager::new(client.clone(), config.clone());
46
47 Self {
48 client,
49 config: Arc::new(config),
50 rate_limiter: RateLimiter::new(),
51 auth_manager: Arc::new(Mutex::new(auth_manager)),
52 }
53 }
54
55 pub fn config(&self) -> &HttpConfig {
57 &self.config
58 }
59
60 pub fn base_url(&self) -> &str {
62 self.config.base_url.as_str()
63 }
64
65 pub fn http_client(&self) -> &Client {
67 &self.client
68 }
69
70 pub async fn make_request(&self, url: &str) -> Result<reqwest::Response, HttpError> {
72 let category = categorize_endpoint(url);
74
75 self.rate_limiter.wait_for_permission(category).await;
77
78 self.client
80 .get(url)
81 .send()
82 .await
83 .map_err(|e| HttpError::NetworkError(e.to_string()))
84 }
85
86 pub async fn make_authenticated_request(
88 &self,
89 url: &str,
90 ) -> Result<reqwest::Response, HttpError> {
91 let category = categorize_endpoint(url);
93
94 self.rate_limiter.wait_for_permission(category).await;
96
97 let auth_header = {
99 let mut auth_manager = self.auth_manager.lock().await;
100 auth_manager
101 .get_authorization_header()
102 .await
103 .ok_or_else(|| {
104 HttpError::AuthenticationFailed(
105 "No valid authentication token available.".to_string(),
106 )
107 })?
108 };
109
110 tracing::debug!("Using authorization header: {}", auth_header);
112
113 self.client
115 .get(url)
116 .header("Authorization", auth_header)
117 .send()
118 .await
119 .map_err(|e| HttpError::NetworkError(e.to_string()))
120 }
121
122 pub async fn make_authenticated_post_request<T: serde::Serialize>(
124 &self,
125 url: &str,
126 body: &T,
127 ) -> Result<reqwest::Response, HttpError> {
128 let category = categorize_endpoint(url);
130
131 self.rate_limiter.wait_for_permission(category).await;
133
134 let auth_header = {
136 let mut auth_manager = self.auth_manager.lock().await;
137 auth_manager
138 .get_authorization_header()
139 .await
140 .ok_or_else(|| {
141 HttpError::AuthenticationFailed(
142 "No valid authentication token available.".to_string(),
143 )
144 })?
145 };
146
147 tracing::debug!("Using authorization header: {}", auth_header);
149
150 self.client
152 .post(url)
153 .header("Authorization", auth_header)
154 .json(body)
155 .send()
156 .await
157 .map_err(|e| HttpError::NetworkError(e.to_string()))
158 }
159
160 pub fn rate_limiter(&self) -> &RateLimiter {
162 &self.rate_limiter
163 }
164
165 pub async fn public_get<T>(&self, endpoint: &str, query: &str) -> Result<T, HttpError>
184 where
185 T: DeserializeOwned,
186 {
187 let url = format!("{}{}{}", self.base_url(), endpoint, query);
188
189 let response = self.make_request(&url).await?;
190
191 if !response.status().is_success() {
192 let error_text = response
193 .text()
194 .await
195 .unwrap_or_else(|_| "Unknown error".to_string());
196 return Err(HttpError::RequestFailed(error_text));
197 }
198
199 let api_response: ApiResponse<T> = response
200 .json()
201 .await
202 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
203
204 if let Some(error) = api_response.error {
205 return Err(HttpError::RequestFailed(format!(
206 "API error: {} - {}",
207 error.code, error.message
208 )));
209 }
210
211 api_response
212 .result
213 .ok_or_else(|| HttpError::InvalidResponse("No result in response".to_string()))
214 }
215
216 pub async fn private_get<T>(&self, endpoint: &str, query: &str) -> Result<T, HttpError>
236 where
237 T: DeserializeOwned,
238 {
239 let url = format!("{}{}{}", self.base_url(), endpoint, query);
240
241 let response = self.make_authenticated_request(&url).await?;
242
243 if !response.status().is_success() {
244 let error_text = response
245 .text()
246 .await
247 .unwrap_or_else(|_| "Unknown error".to_string());
248 return Err(HttpError::RequestFailed(error_text));
249 }
250
251 let body = response.text().await.map_err(|e| {
252 HttpError::InvalidResponse(format!("Failed to read response body: {}", e))
253 })?;
254
255 let api_response: ApiResponse<T> = serde_json::from_str(&body).map_err(|e| {
256 tracing::error!(
257 error = %e,
258 endpoint = %endpoint,
259 body_preview = %&body[..body.len().min(1000)],
260 "Failed to deserialize private API response"
261 );
262 HttpError::InvalidResponse(format!(
263 "error decoding response body: {} - Raw (first 500 chars): {}",
264 e,
265 &body[..body.len().min(500)]
266 ))
267 })?;
268
269 if let Some(error) = api_response.error {
270 return Err(HttpError::RequestFailed(format!(
271 "API error: {} - {}",
272 error.code, error.message
273 )));
274 }
275
276 api_response
277 .result
278 .ok_or_else(|| HttpError::InvalidResponse("No result in response".to_string()))
279 }
280
281 pub async fn exchange_token(
283 &self,
284 refresh_token: &str,
285 subject_id: u64,
286 scope: Option<&str>,
287 ) -> Result<AuthToken, HttpError> {
288 let mut url = format!(
289 "{}/public/exchange_token?refresh_token={}&subject_id={}",
290 self.config.base_url,
291 urlencoding::encode(refresh_token),
292 subject_id
293 );
294
295 if let Some(scope) = scope {
296 url.push_str(&format!("&scope={}", urlencoding::encode(scope)));
297 }
298
299 let response = self
300 .client
301 .get(&url)
302 .header("Content-Type", "application/json")
303 .send()
304 .await
305 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
306
307 if !response.status().is_success() {
308 let error_text = response
309 .text()
310 .await
311 .unwrap_or_else(|_| "Unknown error".to_string());
312 return Err(HttpError::AuthenticationFailed(format!(
313 "Token exchange failed: {}",
314 error_text
315 )));
316 }
317
318 let json_response: serde_json::Value = response
320 .json()
321 .await
322 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
323
324 if let Some(_error) = json_response.get("error") {
326 return Err(HttpError::AuthenticationFailed(format!(
327 "Token exchange failed: {}",
328 json_response
329 )));
330 }
331
332 let result = json_response
334 .get("result")
335 .ok_or_else(|| HttpError::InvalidResponse("No result in response".to_string()))?;
336
337 let token: AuthToken = serde_json::from_value(result.clone())
338 .map_err(|e| HttpError::InvalidResponse(format!("Failed to parse token: {}", e)))?;
339
340 self.auth_manager.lock().await.update_token(token.clone());
342
343 Ok(token)
344 }
345
346 pub async fn fork_token(
348 &self,
349 refresh_token: &str,
350 session_name: &str,
351 scope: Option<&str>,
352 ) -> Result<AuthToken, HttpError> {
353 let mut url = format!(
354 "{}/public/fork_token?refresh_token={}&session_name={}",
355 self.config.base_url,
356 urlencoding::encode(refresh_token),
357 urlencoding::encode(session_name)
358 );
359
360 if let Some(scope) = scope {
361 url.push_str(&format!("&scope={}", urlencoding::encode(scope)));
362 }
363
364 let response = self
365 .client
366 .get(&url)
367 .header("Content-Type", "application/json")
368 .send()
369 .await
370 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
371
372 if !response.status().is_success() {
373 let error_text = response
374 .text()
375 .await
376 .unwrap_or_else(|_| "Unknown error".to_string());
377 return Err(HttpError::AuthenticationFailed(format!(
378 "Token fork failed: {}",
379 error_text
380 )));
381 }
382
383 let json_response: serde_json::Value = response
385 .json()
386 .await
387 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
388
389 if let Some(_error) = json_response.get("error") {
391 return Err(HttpError::AuthenticationFailed(format!(
392 "Token fork failed: {}",
393 json_response
394 )));
395 }
396
397 let result = json_response
399 .get("result")
400 .ok_or_else(|| HttpError::InvalidResponse("No result in response".to_string()))?;
401
402 let token: AuthToken = serde_json::from_value(result.clone())
403 .map_err(|e| HttpError::InvalidResponse(format!("Failed to parse token: {}", e)))?;
404
405 self.auth_manager.lock().await.update_token(token.clone());
406
407 Ok(token)
408 }
409}
410
411impl Default for DeribitHttpClient {
412 fn default() -> Self {
413 Self::new()
414 }
415}