Skip to main content

ccxt_core/http_client/
config.rs

1use crate::circuit_breaker::CircuitBreakerConfig;
2use crate::config::ProxyConfig;
3use crate::error::{ConfigValidationError, ValidationResult};
4use crate::retry_strategy::RetryConfig;
5use std::time::Duration;
6
7/// HTTP request configuration
8#[derive(Debug, Clone)]
9pub 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
68impl 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: 128 * 1024 * 1024, // 128MB default
82            max_request_size: 10 * 1024 * 1024,   // 10MB default
83            circuit_breaker: None,
84            pool_max_idle_per_host: 10,
85            pool_idle_timeout: Duration::from_secs(90),
86        }
87    }
88}
89
90impl 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}