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 api_response: ApiResponse<T> = response
252 .json()
253 .await
254 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
255
256 if let Some(error) = api_response.error {
257 return Err(HttpError::RequestFailed(format!(
258 "API error: {} - {}",
259 error.code, error.message
260 )));
261 }
262
263 api_response
264 .result
265 .ok_or_else(|| HttpError::InvalidResponse("No result in response".to_string()))
266 }
267
268 pub async fn exchange_token(
270 &self,
271 refresh_token: &str,
272 subject_id: u64,
273 scope: Option<&str>,
274 ) -> Result<AuthToken, HttpError> {
275 let mut url = format!(
276 "{}/public/exchange_token?refresh_token={}&subject_id={}",
277 self.config.base_url,
278 urlencoding::encode(refresh_token),
279 subject_id
280 );
281
282 if let Some(scope) = scope {
283 url.push_str(&format!("&scope={}", urlencoding::encode(scope)));
284 }
285
286 let response = self
287 .client
288 .get(&url)
289 .header("Content-Type", "application/json")
290 .send()
291 .await
292 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
293
294 if !response.status().is_success() {
295 let error_text = response
296 .text()
297 .await
298 .unwrap_or_else(|_| "Unknown error".to_string());
299 return Err(HttpError::AuthenticationFailed(format!(
300 "Token exchange failed: {}",
301 error_text
302 )));
303 }
304
305 let json_response: serde_json::Value = response
307 .json()
308 .await
309 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
310
311 if let Some(_error) = json_response.get("error") {
313 return Err(HttpError::AuthenticationFailed(format!(
314 "Token exchange failed: {}",
315 json_response
316 )));
317 }
318
319 let result = json_response
321 .get("result")
322 .ok_or_else(|| HttpError::InvalidResponse("No result in response".to_string()))?;
323
324 let token: AuthToken = serde_json::from_value(result.clone())
325 .map_err(|e| HttpError::InvalidResponse(format!("Failed to parse token: {}", e)))?;
326
327 self.auth_manager.lock().await.update_token(token.clone());
329
330 Ok(token)
331 }
332
333 pub async fn fork_token(
335 &self,
336 refresh_token: &str,
337 session_name: &str,
338 scope: Option<&str>,
339 ) -> Result<AuthToken, HttpError> {
340 let mut url = format!(
341 "{}/public/fork_token?refresh_token={}&session_name={}",
342 self.config.base_url,
343 urlencoding::encode(refresh_token),
344 urlencoding::encode(session_name)
345 );
346
347 if let Some(scope) = scope {
348 url.push_str(&format!("&scope={}", urlencoding::encode(scope)));
349 }
350
351 let response = self
352 .client
353 .get(&url)
354 .header("Content-Type", "application/json")
355 .send()
356 .await
357 .map_err(|e| HttpError::NetworkError(e.to_string()))?;
358
359 if !response.status().is_success() {
360 let error_text = response
361 .text()
362 .await
363 .unwrap_or_else(|_| "Unknown error".to_string());
364 return Err(HttpError::AuthenticationFailed(format!(
365 "Token fork failed: {}",
366 error_text
367 )));
368 }
369
370 let json_response: serde_json::Value = response
372 .json()
373 .await
374 .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
375
376 if let Some(_error) = json_response.get("error") {
378 return Err(HttpError::AuthenticationFailed(format!(
379 "Token fork failed: {}",
380 json_response
381 )));
382 }
383
384 let result = json_response
386 .get("result")
387 .ok_or_else(|| HttpError::InvalidResponse("No result in response".to_string()))?;
388
389 let token: AuthToken = serde_json::from_value(result.clone())
390 .map_err(|e| HttpError::InvalidResponse(format!("Failed to parse token: {}", e)))?;
391
392 self.auth_manager.lock().await.update_token(token.clone());
393
394 Ok(token)
395 }
396}
397
398impl Default for DeribitHttpClient {
399 fn default() -> Self {
400 Self::new()
401 }
402}