Skip to content

Commit b8ec2f4

Browse files
PrayingPraying
authored andcommitted
feat(http): remove HTTP client implementation and related modules
- Removed entire http_client.rs module with HttpClient and HttpConfig implementations - Deleted all HTTP request functionality including fetch, get, post, put, delete methods - Removed retry mechanism and timeout control features - Eliminated proxy configuration and rate limiting integration - Removed circuit breaker support and gzip compression/decompression - Deleted request/response logging and structured tracing functionality - Removed all associated tests for HTTP client functionality - Removed dependencies on reqwest, serde_json and other HTTP-related crates
1 parent 41c5c3a commit b8ec2f4

9 files changed

Lines changed: 1529 additions & 1497 deletions

File tree

‎ccxt-core/src/http_client.rs‎

Lines changed: 0 additions & 1497 deletions
This file was deleted.
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
use crate::circuit_breaker::CircuitBreaker;
2+
use crate::error::{Error, Result};
3+
use crate::rate_limiter::RateLimiter;
4+
use crate::retry_strategy::RetryStrategy;
5+
use reqwest::Client;
6+
use std::sync::Arc;
7+
8+
use super::config::HttpConfig;
9+
10+
/// HTTP client with retry and rate limiting support
11+
#[derive(Debug)]
12+
pub struct HttpClient {
13+
client: Client,
14+
config: HttpConfig,
15+
rate_limiter: Option<RateLimiter>,
16+
retry_strategy: RetryStrategy,
17+
circuit_breaker: Option<Arc<CircuitBreaker>>,
18+
}
19+
20+
impl HttpClient {
21+
/// Creates a new HTTP client with the given configuration.
22+
///
23+
/// # Arguments
24+
///
25+
/// * `config` - HTTP client configuration
26+
///
27+
/// # Returns
28+
///
29+
/// Returns a `Result` containing the initialized client or an error if client creation fails.
30+
///
31+
/// # Errors
32+
///
33+
/// Returns an error if:
34+
/// - The proxy URL is invalid
35+
/// - The HTTP client cannot be built
36+
pub fn new(config: HttpConfig) -> Result<Self> {
37+
let mut builder = Client::builder()
38+
.timeout(config.timeout)
39+
.connect_timeout(config.connect_timeout)
40+
.pool_max_idle_per_host(config.pool_max_idle_per_host)
41+
.pool_idle_timeout(config.pool_idle_timeout)
42+
.gzip(true)
43+
.user_agent(&config.user_agent);
44+
45+
if let Some(proxy_config) = &config.proxy {
46+
let mut proxy = reqwest::Proxy::all(&proxy_config.url)
47+
.map_err(|e| Error::network(format!("Invalid proxy URL: {e}")))?;
48+
49+
if let (Some(username), Some(password)) =
50+
(&proxy_config.username, &proxy_config.password)
51+
{
52+
proxy = proxy.basic_auth(username, password);
53+
}
54+
builder = builder.proxy(proxy);
55+
}
56+
57+
let client = builder
58+
.build()
59+
.map_err(|e| Error::network(format!("Failed to build HTTP client: {e}")))?;
60+
61+
let retry_strategy = RetryStrategy::new(config.retry_config.clone().unwrap_or_default());
62+
63+
let circuit_breaker = config
64+
.circuit_breaker
65+
.as_ref()
66+
.map(|cb_config| Arc::new(CircuitBreaker::new(cb_config.clone())));
67+
68+
Ok(Self {
69+
client,
70+
config,
71+
rate_limiter: None,
72+
retry_strategy,
73+
circuit_breaker,
74+
})
75+
}
76+
77+
/// Creates a new HTTP client with a custom rate limiter.
78+
///
79+
/// # Arguments
80+
///
81+
/// * `config` - HTTP client configuration
82+
/// * `rate_limiter` - Pre-configured rate limiter instance
83+
///
84+
/// # Returns
85+
///
86+
/// Returns a `Result` containing the initialized client with rate limiter.
87+
///
88+
/// # Errors
89+
///
90+
/// Returns an error if client creation fails.
91+
pub fn new_with_rate_limiter(config: HttpConfig, rate_limiter: RateLimiter) -> Result<Self> {
92+
let mut client = Self::new(config)?;
93+
client.rate_limiter = Some(rate_limiter);
94+
Ok(client)
95+
}
96+
97+
/// Sets the rate limiter for this client.
98+
///
99+
/// # Arguments
100+
///
101+
/// * `rate_limiter` - Rate limiter instance to use
102+
pub fn set_rate_limiter(&mut self, rate_limiter: RateLimiter) {
103+
self.rate_limiter = Some(rate_limiter);
104+
}
105+
106+
/// Sets the retry strategy for this client.
107+
///
108+
/// # Arguments
109+
///
110+
/// * `strategy` - Retry strategy to use for failed requests
111+
pub fn set_retry_strategy(&mut self, strategy: RetryStrategy) {
112+
self.retry_strategy = strategy;
113+
}
114+
115+
/// Sets the circuit breaker for this client.
116+
///
117+
/// # Arguments
118+
///
119+
/// * `circuit_breaker` - Circuit breaker instance to use
120+
///
121+
/// # Example
122+
///
123+
/// ```rust
124+
/// use ccxt_core::http_client::{HttpClient, HttpConfig};
125+
/// use ccxt_core::circuit_breaker::{CircuitBreaker, CircuitBreakerConfig};
126+
/// use std::sync::Arc;
127+
///
128+
/// let mut client = HttpClient::new(HttpConfig::default()).unwrap();
129+
/// let cb = Arc::new(CircuitBreaker::new(CircuitBreakerConfig::default()));
130+
/// client.set_circuit_breaker(cb);
131+
/// ```
132+
pub fn set_circuit_breaker(&mut self, circuit_breaker: Arc<CircuitBreaker>) {
133+
self.circuit_breaker = Some(circuit_breaker);
134+
}
135+
136+
/// Returns a reference to the circuit breaker, if configured.
137+
///
138+
/// This can be used to check the circuit breaker state or manually
139+
/// record success/failure.
140+
pub fn circuit_breaker(&self) -> Option<&Arc<CircuitBreaker>> {
141+
self.circuit_breaker.as_ref()
142+
}
143+
144+
/// Returns a reference to current HTTP configuration.
145+
pub fn config(&self) -> &HttpConfig {
146+
&self.config
147+
}
148+
149+
/// Updates the HTTP configuration.
150+
///
151+
/// # Arguments
152+
///
153+
/// * `config` - New configuration to use
154+
pub fn set_config(&mut self, config: HttpConfig) {
155+
self.config = config;
156+
}
157+
158+
/// Internal: Returns reference to the underlying reqwest client.
159+
pub(crate) fn client(&self) -> &Client {
160+
&self.client
161+
}
162+
163+
/// Internal: Returns reference to the rate limiter, if configured.
164+
pub(crate) fn rate_limiter(&self) -> Option<&RateLimiter> {
165+
self.rate_limiter.as_ref()
166+
}
167+
168+
/// Internal: Returns reference to the retry strategy.
169+
pub(crate) fn retry_strategy(&self) -> &RetryStrategy {
170+
&self.retry_strategy
171+
}
172+
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
use crate::circuit_breaker::CircuitBreakerConfig;
2+
use crate::config::ProxyConfig;
3+
use crate::error::{ConfigValidationError, ValidationResult};
4+
use crate::retry_strategy::RetryConfig;
5+
use std::time::Duration;
6+
7+
/// HTTP request configuration
8+
#[derive(Debug, Clone)]
9+
pub struct HttpConfig {
10+
/// Request timeout
11+
pub timeout: Duration,
12+
/// TCP connection timeout (default: 10 seconds)
13+
pub connect_timeout: Duration,
14+
/// Maximum retry attempts (deprecated, use `retry_config` instead)
15+
#[deprecated(note = "Use retry_config instead")]
16+
pub max_retries: u32,
17+
/// Whether to enable verbose logging
18+
pub verbose: bool,
19+
/// Default User-Agent header value
20+
pub user_agent: String,
21+
/// Whether to include response headers in the result
22+
pub return_response_headers: bool,
23+
/// Optional proxy configuration
24+
pub proxy: Option<ProxyConfig>,
25+
/// Whether to enable rate limiting
26+
pub enable_rate_limit: bool,
27+
/// Optional retry configuration (uses default if `None`)
28+
pub retry_config: Option<RetryConfig>,
29+
/// Maximum response body size in bytes (default: 10MB)
30+
///
31+
/// Responses exceeding this limit will be rejected with an `InvalidRequest` error.
32+
/// This protects against malicious or abnormal responses that could exhaust memory.
33+
pub max_response_size: usize,
34+
35+
/// Maximum request body size in bytes (default: 10MB)
36+
///
37+
/// Request bodies exceeding this limit will be rejected BEFORE serialization.
38+
/// This protects against DoS attacks via oversized request payloads.
39+
pub max_request_size: usize,
40+
41+
/// Optional circuit breaker configuration.
42+
///
43+
/// When enabled, the circuit breaker will track request failures and
44+
/// automatically block requests to failing endpoints, allowing the
45+
/// system to recover.
46+
///
47+
/// Default: `None` (disabled for backward compatibility)
48+
pub circuit_breaker: Option<CircuitBreakerConfig>,
49+
50+
/// Maximum number of idle connections per host in the connection pool.
51+
///
52+
/// This controls how many keep-alive connections are maintained for each host.
53+
/// Higher values improve performance for repeated requests to the same host
54+
/// but consume more resources.
55+
///
56+
/// Default: 10
57+
pub pool_max_idle_per_host: usize,
58+
59+
/// Timeout for idle connections in the pool.
60+
///
61+
/// Connections that have been idle longer than this duration will be closed.
62+
/// This helps free up resources and avoid stale connections.
63+
///
64+
/// Default: 90 seconds
65+
pub pool_idle_timeout: Duration,
66+
}
67+
68+
impl Default for HttpConfig {
69+
fn default() -> Self {
70+
Self {
71+
timeout: Duration::from_secs(30),
72+
connect_timeout: Duration::from_secs(10),
73+
#[allow(deprecated)]
74+
max_retries: 3,
75+
verbose: false,
76+
user_agent: "ccxt-rust/1.0".to_string(),
77+
return_response_headers: false,
78+
proxy: None,
79+
enable_rate_limit: true,
80+
retry_config: None,
81+
max_response_size: 10 * 1024 * 1024,
82+
max_request_size: 10 * 1024 * 1024,
83+
circuit_breaker: None,
84+
pool_max_idle_per_host: 10,
85+
pool_idle_timeout: Duration::from_secs(90),
86+
}
87+
}
88+
}
89+
90+
impl HttpConfig {
91+
/// Validates the HTTP configuration parameters.
92+
///
93+
/// # Returns
94+
///
95+
/// Returns `Ok(ValidationResult)` if the configuration is valid.
96+
/// The `ValidationResult` may contain warnings for suboptimal but valid configurations.
97+
///
98+
/// Returns `Err(ConfigValidationError)` if the configuration is invalid.
99+
///
100+
/// # Validation Rules
101+
///
102+
/// - `timeout` > 5 minutes returns an error (excessive timeout)
103+
/// - `timeout` < 1 second generates a warning (may cause frequent timeouts)
104+
///
105+
/// # Example
106+
///
107+
/// ```rust
108+
/// use ccxt_core::http_client::HttpConfig;
109+
/// use std::time::Duration;
110+
///
111+
/// let config = HttpConfig::default();
112+
/// let result = config.validate();
113+
/// assert!(result.is_ok());
114+
///
115+
/// let invalid_config = HttpConfig {
116+
/// timeout: Duration::from_secs(600), // 10 minutes - too long
117+
/// ..Default::default()
118+
/// };
119+
/// let result = invalid_config.validate();
120+
/// assert!(result.is_err());
121+
/// ```
122+
pub fn validate(&self) -> std::result::Result<ValidationResult, ConfigValidationError> {
123+
const MAX_REASONABLE_REQUEST_SIZE: usize = 100 * 1024 * 1024;
124+
125+
let mut warnings = Vec::new();
126+
let max_timeout = Duration::from_secs(300);
127+
if self.timeout > max_timeout {
128+
return Err(ConfigValidationError::too_high(
129+
"timeout",
130+
format!("{:?}", self.timeout),
131+
"5 minutes",
132+
));
133+
}
134+
135+
if self.timeout < Duration::from_secs(1) {
136+
warnings.push(format!(
137+
"timeout {:?} is very short, may cause frequent timeouts",
138+
self.timeout
139+
));
140+
}
141+
142+
if self.max_request_size == 0 {
143+
return Err(ConfigValidationError::invalid(
144+
"max_request_size",
145+
"max_request_size cannot be zero",
146+
));
147+
}
148+
149+
if self.max_request_size > MAX_REASONABLE_REQUEST_SIZE {
150+
return Err(ConfigValidationError::too_high(
151+
"max_request_size",
152+
format!("{}", self.max_request_size),
153+
"100MB (104857600 bytes)",
154+
));
155+
}
156+
157+
Ok(ValidationResult::with_warnings(warnings))
158+
}
159+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use reqwest::header::HeaderMap;
2+
use serde_json::Value;
3+
4+
pub(crate) fn headers_to_json(headers: &HeaderMap) -> Value {
5+
let mut map = serde_json::Map::new();
6+
for (key, value) in headers {
7+
let key_str = key.as_str().to_string();
8+
let value_str = value.to_str().unwrap_or("").to_string();
9+
map.insert(key_str, Value::String(value_str));
10+
}
11+
Value::Object(map)
12+
}

‎ccxt-core/src/http_client/mod.rs‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//! HTTP client abstraction layer
2+
//!
3+
//! Provides a unified HTTP request interface with support for:
4+
//! - Automatic retry mechanism with configurable strategies
5+
//! - Timeout control per request
6+
//! - Request and response size limits for DoS protection
7+
//! - Circuit breaker integration for resilience
8+
//! - Rate limiting integration
9+
//!
10+
//! # Example
11+
//!
12+
//! ```rust,no_run
13+
//! use ccxt_core::http_client::{HttpClient, HttpConfig};
14+
//!
15+
//! let config = HttpConfig::default();
16+
//! let client = HttpClient::new(config)?;
17+
//!
18+
//! let response = client.get("https://example.com/api", None).await?;
19+
//! ```
20+
//!
21+
//! # Features
22+
//!
23+
//! - **Retry**: Automatic retry with exponential backoff for transient failures
24+
//! - **Circuit Breaker**: Prevents cascading failures by blocking requests to failing endpoints
25+
//! - **Rate Limiting**: Optional rate limiting for API quota management
26+
//! - **Size Limits**: Protection against oversized requests/responses
27+
28+
mod builder;
29+
mod config;
30+
mod headers;
31+
mod request;
32+
mod response;
33+
mod retry;
34+
35+
#[cfg(test)]
36+
mod tests;
37+
38+
pub use builder::HttpClient;
39+
pub use config::HttpConfig;

0 commit comments

Comments
 (0)