deribit_http/
client.rs

1//! HTTP client implementation for Deribit REST API
2
3use crate::auth::AuthManager;
4use crate::config::HttpConfig;
5use crate::error::HttpError;
6use crate::model::types::AuthToken;
7use crate::rate_limit::{RateLimiter, categorize_endpoint};
8use reqwest::Client;
9use std::sync::Arc;
10use tokio::sync::Mutex;
11
12/// HTTP client for Deribit REST API
13#[derive(Debug, Clone)]
14pub struct DeribitHttpClient {
15    /// HTTP client instance
16    client: Client,
17    /// Configuration
18    config: Arc<HttpConfig>,
19    /// Rate limiter
20    rate_limiter: RateLimiter,
21    /// Authentication manager
22    auth_manager: Arc<Mutex<AuthManager>>,
23}
24
25impl DeribitHttpClient {
26    /// Create a new HTTP client
27    pub fn new() -> Self {
28        let config = HttpConfig::default();
29        Self::with_config(config)
30    }
31
32    /// Create a new HTTP client with custom configuration
33    pub fn with_config(config: HttpConfig) -> Self {
34        let opt_client = Client::builder()
35            .timeout(config.timeout)
36            .user_agent(&config.user_agent)
37            .build()
38            .map_err(|e| HttpError::NetworkError(e.to_string()))
39            .ok();
40
41        let client = if let Some(opt_client) = &opt_client {
42            opt_client.clone()
43        } else {
44            panic!("Failed to create HTTP client");
45        };
46
47        let auth_manager = AuthManager::new(client.clone(), config.clone());
48
49        Self {
50            client,
51            config: Arc::new(config),
52            rate_limiter: RateLimiter::new(),
53            auth_manager: Arc::new(Mutex::new(auth_manager)),
54        }
55    }
56
57    /// Get the configuration
58    pub fn config(&self) -> &HttpConfig {
59        &self.config
60    }
61
62    /// Get the base URL
63    pub fn base_url(&self) -> &str {
64        self.config.base_url.as_str()
65    }
66
67    /// Get the HTTP client
68    pub fn http_client(&self) -> &Client {
69        &self.client
70    }
71
72    /// Make a rate-limited HTTP request
73    pub async fn make_request(&self, url: &str) -> Result<reqwest::Response, HttpError> {
74        // Determine rate limit category from URL
75        let category = categorize_endpoint(url);
76
77        // Wait for rate limit permission
78        self.rate_limiter.wait_for_permission(category).await;
79
80        // Make the request
81        self.client
82            .get(url)
83            .send()
84            .await
85            .map_err(|e| HttpError::NetworkError(e.to_string()))
86    }
87
88    /// Make an authenticated HTTP GET request for private endpoints
89    pub async fn make_authenticated_request(
90        &self,
91        url: &str,
92    ) -> Result<reqwest::Response, HttpError> {
93        // Determine rate limit category from URL
94        let category = categorize_endpoint(url);
95
96        // Wait for rate limit permission
97        self.rate_limiter.wait_for_permission(category).await;
98
99        // Get authorization header
100        let mut auth_manager = self.auth_manager.lock().await;
101        let auth_header = auth_manager
102            .get_authorization_header()
103            .await
104            .ok_or_else(|| {
105                HttpError::AuthenticationFailed(
106                    "No valid authentication token available.".to_string(),
107                )
108            })?;
109
110        // Debug: log the authorization header being used
111        tracing::debug!("Using authorization header: {}", auth_header);
112        drop(auth_manager);
113
114        // Make the authenticated request
115        self.client
116            .get(url)
117            .header("Authorization", auth_header)
118            .send()
119            .await
120            .map_err(|e| HttpError::NetworkError(e.to_string()))
121    }
122
123    /// Make an authenticated HTTP POST request for private endpoints
124    pub async fn make_authenticated_post_request<T: serde::Serialize>(
125        &self,
126        url: &str,
127        body: &T,
128    ) -> Result<reqwest::Response, HttpError> {
129        // Determine rate limit category from URL
130        let category = categorize_endpoint(url);
131
132        // Wait for rate limit permission
133        self.rate_limiter.wait_for_permission(category).await;
134
135        // Get authorization header
136        let mut auth_manager = self.auth_manager.lock().await;
137        let auth_header = 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        // Debug: log the authorization header being used
147        tracing::debug!("Using authorization header: {}", auth_header);
148        drop(auth_manager);
149
150        // Make the authenticated POST request
151        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    /// Get rate limiter for advanced usage
161    pub fn rate_limiter(&self) -> &RateLimiter {
162        &self.rate_limiter
163    }
164
165    /// Exchange refresh token for a new access token with different subject_id
166    pub async fn exchange_token(
167        &self,
168        refresh_token: &str,
169        subject_id: u64,
170        scope: Option<&str>,
171    ) -> Result<AuthToken, HttpError> {
172        let mut url = format!(
173            "{}/public/exchange_token?refresh_token={}&subject_id={}",
174            self.config.base_url,
175            urlencoding::encode(refresh_token),
176            subject_id
177        );
178
179        if let Some(scope) = scope {
180            url.push_str(&format!("&scope={}", urlencoding::encode(scope)));
181        }
182
183        let response = self
184            .client
185            .get(&url)
186            .header("Content-Type", "application/json")
187            .send()
188            .await
189            .map_err(|e| HttpError::NetworkError(e.to_string()))?;
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::AuthenticationFailed(format!(
197                "Token exchange failed: {}",
198                error_text
199            )));
200        }
201
202        // Parse the JSON-RPC response directly
203        let json_response: serde_json::Value = response
204            .json()
205            .await
206            .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
207
208        // Check for JSON-RPC error
209        if let Some(_error) = json_response.get("error") {
210            return Err(HttpError::AuthenticationFailed(format!(
211                "Token exchange failed: {}",
212                json_response
213            )));
214        }
215
216        // Extract the result and parse as AuthToken
217        let result = json_response
218            .get("result")
219            .ok_or_else(|| HttpError::InvalidResponse("No result in response".to_string()))?;
220
221        let token: AuthToken = serde_json::from_value(result.clone())
222            .map_err(|e| HttpError::InvalidResponse(format!("Failed to parse token: {}", e)))?;
223
224        // Update the stored token
225        let _auth_manager = self.auth_manager.lock().await;
226        let _expires_at =
227            std::time::SystemTime::now() + std::time::Duration::from_secs(token.expires_in);
228
229        self.auth_manager.lock().await.update_token(token.clone());
230
231        Ok(token)
232    }
233
234    /// Fork a token to create a new session with the same permissions
235    pub async fn fork_token(
236        &self,
237        refresh_token: &str,
238        session_name: &str,
239        scope: Option<&str>,
240    ) -> Result<AuthToken, HttpError> {
241        let mut url = format!(
242            "{}/public/fork_token?refresh_token={}&session_name={}",
243            self.config.base_url,
244            urlencoding::encode(refresh_token),
245            urlencoding::encode(session_name)
246        );
247
248        if let Some(scope) = scope {
249            url.push_str(&format!("&scope={}", urlencoding::encode(scope)));
250        }
251
252        let response = self
253            .client
254            .get(&url)
255            .header("Content-Type", "application/json")
256            .send()
257            .await
258            .map_err(|e| HttpError::NetworkError(e.to_string()))?;
259
260        if !response.status().is_success() {
261            let error_text = response
262                .text()
263                .await
264                .unwrap_or_else(|_| "Unknown error".to_string());
265            return Err(HttpError::AuthenticationFailed(format!(
266                "Token fork failed: {}",
267                error_text
268            )));
269        }
270
271        // Parse the JSON-RPC response directly
272        let json_response: serde_json::Value = response
273            .json()
274            .await
275            .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
276
277        // Check for JSON-RPC error
278        if let Some(_error) = json_response.get("error") {
279            return Err(HttpError::AuthenticationFailed(format!(
280                "Token fork failed: {}",
281                json_response
282            )));
283        }
284
285        // Extract the result and parse as AuthToken
286        let result = json_response
287            .get("result")
288            .ok_or_else(|| HttpError::InvalidResponse("No result in response".to_string()))?;
289
290        let token: AuthToken = serde_json::from_value(result.clone())
291            .map_err(|e| HttpError::InvalidResponse(format!("Failed to parse token: {}", e)))?;
292
293        self.auth_manager.lock().await.update_token(token.clone());
294
295        Ok(token)
296    }
297}
298
299impl Default for DeribitHttpClient {
300    fn default() -> Self {
301        Self::new()
302    }
303}