gooty_proxy/definitions/proxy.rs
1//! # Proxy Module
2//!
3//! This module provides definitions and operations for proxy servers, including their
4//! configuration, validation, and metadata management.
5//!
6//! ## Overview
7//!
8//! The module centers around the `Proxy` struct, which represents a proxy server with
9//! its connection details (address, port, protocol) and extended metadata (anonymity level,
10//! location, organization info). It includes functionality for:
11//!
12//! - Creating and configuring proxy instances
13//! - Validating proxy configurations
14//! - Tracking proxy performance metrics
15//! - Managing proxy metadata
16//! - Serializing and deserializing proxy data
17//!
18//! ## Examples
19//!
20//! ```
21//! use gooty_proxy::definitions::proxy::Proxy;
22//! use gooty_proxy::definitions::enums::{ProxyType, AnonymityLevel};
23//! use std::net::{IpAddr, Ipv4Addr};
24//!
25//! // Create a new HTTP proxy
26//! let proxy = Proxy::new(
27//! ProxyType::Http,
28//! IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)),
29//! 8080,
30//! AnonymityLevel::Elite,
31//! );
32//!
33//! // Add authentication credentials
34//! let authenticated_proxy = proxy.clone()
35//! .with_auth("username".to_string(), "password".to_string());
36//!
37//! // Get connection string
38//! let connection_string = authenticated_proxy.to_connection_string();
39//! assert_eq!(connection_string, "http://username:password@192.168.1.1:8080");
40//! ```
41
42use crate::definitions::{
43 enums::{AnonymityLevel, ProxyType},
44 errors::ProxyError,
45};
46use crate::inspection::{IpMetadata, Location, NetworkInfo, Organization};
47use chrono::{DateTime, Utc};
48use serde::{Deserialize, Serialize};
49use std::net::IpAddr;
50
51/// Represents a proxy server with its connection details and metadata.
52///
53/// This struct is used throughout the application to manage and interact with
54/// proxy servers. It includes fields for the proxy's type, address, port, and
55/// anonymity level, as well as methods for managing its state and statistics.
56///
57/// # Examples
58///
59/// ```
60/// use gooty_proxy::definitions::Proxy;
61/// use gooty_proxy::definitions::enums::{ProxyType, AnonymityLevel};
62/// use std::net::{IpAddr, Ipv4Addr};
63///
64/// let proxy = Proxy::new(
65/// ProxyType::Http,
66/// IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
67/// 8080,
68/// AnonymityLevel::Elite,
69/// );
70///
71/// assert_eq!(proxy.proxy_type, ProxyType::Http);
72/// assert_eq!(proxy.port, 8080);
73/// ```
74#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
75pub struct Proxy {
76 /// The type of the proxy (e.g., HTTP, HTTPS, SOCKS4, SOCKS5).
77 pub proxy_type: ProxyType,
78
79 /// The IP address of the proxy server.
80 pub address: IpAddr,
81
82 /// The port number of the proxy server.
83 pub port: u16,
84
85 /// Optional username for authentication.
86 pub username: Option<String>,
87
88 /// Optional password for authentication.
89 pub password: Option<String>,
90
91 /// The anonymity level of the proxy.
92 pub anonymity: AnonymityLevel,
93
94 /// The country associated with the proxy, if available.
95 pub country: Option<String>,
96
97 /// The organization associated with the proxy, if available.
98 pub organization: Option<String>,
99
100 /// The ASN (Autonomous System Number) of the proxy, if available.
101 pub asn: Option<String>,
102
103 /// The hostname of the proxy, if available.
104 pub hostname: Option<String>,
105
106 /// The latency of the proxy in milliseconds, if measured.
107 pub latency_ms: Option<u128>,
108
109 /// When the proxy was added to the system.
110 pub added_at: DateTime<Utc>,
111
112 /// When the proxy was last checked for availability.
113 pub last_checked_at: Option<DateTime<Utc>>,
114
115 /// The total number of checks performed on the proxy.
116 pub check_count: usize,
117
118 /// The number of failed checks for the proxy.
119 pub check_failure_count: usize,
120
121 /// When the proxy was last used for a connection.
122 pub last_used_at: Option<DateTime<Utc>>,
123
124 /// Number of times the proxy has been used for connections.
125 pub use_count: usize,
126
127 /// Number of times connections through this proxy have failed.
128 pub use_failure_count: usize,
129
130 /// Extended network metadata for the proxy IP address.
131 pub ip_metadata: Option<IpMetadata>,
132
133 /// CIDR notation for the network the proxy belongs to.
134 pub cidr: Option<String>,
135
136 /// Optional location information for the proxy IP address.
137 pub location: Option<Location>,
138
139 /// Optional network information for the proxy IP address.
140 pub network: Option<NetworkInfo>,
141
142 /// Optional organization information for the proxy IP address.
143 pub organization_info: Option<Organization>,
144}
145
146impl Proxy {
147 /// Creates a new proxy with mandatory fields and default values for statistics.
148 ///
149 /// # Arguments
150 ///
151 /// * `proxy_type` - The type of proxy protocol to use (HTTP, HTTPS, SOCKS4, SOCKS5)
152 /// * `address` - The IP address of the proxy server
153 /// * `port` - The port number the proxy server listens on
154 /// * `anonymity` - The level of anonymity provided by the proxy
155 ///
156 /// # Returns
157 ///
158 /// A new `Proxy` instance with default values for non-specified fields
159 ///
160 /// # Examples
161 ///
162 /// ```
163 /// use spiderling_proxy::definitions::{
164 /// enums::{AnonymityLevel, ProxyType},
165 /// proxy::Proxy,
166 /// };
167 /// use std::net::{IpAddr, Ipv4Addr};
168 ///
169 /// let proxy = Proxy::new(
170 /// ProxyType::Http,
171 /// IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)),
172 /// 8080,
173 /// AnonymityLevel::Anonymous,
174 /// );
175 /// ```
176 #[must_use]
177 pub fn new(
178 proxy_type: ProxyType,
179 address: IpAddr,
180 port: u16,
181 anonymity: AnonymityLevel,
182 ) -> Self {
183 Proxy {
184 proxy_type,
185 address,
186 port,
187 username: None,
188 password: None,
189 anonymity,
190 country: None,
191 hostname: None,
192 organization: None,
193 latency_ms: None,
194 added_at: Utc::now(),
195 last_checked_at: None,
196 check_count: 0,
197 check_failure_count: 0,
198 last_used_at: None,
199 use_count: 0,
200 use_failure_count: 0,
201 ip_metadata: None,
202 cidr: None,
203 asn: None,
204 location: None,
205 network: None,
206 organization_info: None,
207 }
208 }
209
210 /// Sets authentication credentials for the proxy.
211 ///
212 /// # Arguments
213 ///
214 /// * `username` - Username for proxy authentication
215 /// * `password` - Password for proxy authentication
216 ///
217 /// # Returns
218 ///
219 /// Self with authentication credentials set
220 ///
221 /// # Examples
222 ///
223 /// ```
224 /// # use spiderling_proxy::definitions::{enums::{AnonymityLevel, ProxyType}, proxy::Proxy};
225 /// # use std::net::{IpAddr, Ipv4Addr};
226 /// let proxy = Proxy::new(
227 /// ProxyType::Http,
228 /// IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)),
229 /// 8080,
230 /// AnonymityLevel::Anonymous
231 /// ).with_auth("username".to_string(), "password".to_string());
232 /// ```
233 #[must_use]
234 pub fn with_auth(mut self, username: String, password: String) -> Self {
235 self.username = Some(username);
236 self.password = Some(password);
237 self
238 }
239
240 /// Sets the country for the proxy.
241 ///
242 /// # Arguments
243 ///
244 /// * `country` - The country where the proxy server is located
245 ///
246 /// # Returns
247 ///
248 /// Self with country information set
249 #[must_use]
250 pub fn with_country(mut self, country: String) -> Self {
251 self.country = Some(country);
252 self
253 }
254
255 /// Sets the hostname for the proxy.
256 ///
257 /// # Arguments
258 ///
259 /// * `hostname` - The hostname of the proxy server
260 ///
261 /// # Returns
262 ///
263 /// Self with hostname information set
264 #[must_use]
265 pub fn with_hostname(mut self, hostname: String) -> Self {
266 self.hostname = Some(hostname);
267 self
268 }
269
270 /// Sets the organization for the proxy.
271 ///
272 /// # Arguments
273 ///
274 /// * `organization` - The organization or ISP operating the proxy
275 ///
276 /// # Returns
277 ///
278 /// Self with organization information set
279 #[must_use]
280 pub fn with_organization(mut self, organization: String) -> Self {
281 self.organization = Some(organization);
282 self
283 }
284
285 /// Validates that the proxy configuration is correct.
286 ///
287 /// # Returns
288 ///
289 /// * `Ok(())` - If the proxy configuration is valid
290 /// * `Err(ProxyError)` - If the proxy configuration is invalid
291 ///
292 /// # Errors
293 ///
294 /// This function will return an error if:
295 /// * The port is set to 0
296 /// * Authentication is missing required fields (e.g., password is missing when username is provided for SOCKS5)
297 pub fn validate(&self) -> Result<(), ProxyError> {
298 // Validate port range (though u16 already ensures this)
299 if self.port == 0 {
300 return Err(ProxyError::InvalidPort(self.port));
301 }
302
303 // Check if authentication is provided when required
304 if matches!(self.proxy_type, ProxyType::Socks5)
305 && self.username.is_some()
306 && self.password.is_none()
307 {
308 return Err(ProxyError::MissingAuthentication);
309 }
310
311 Ok(())
312 }
313
314 /// Records a successful check of the proxy
315 pub fn record_check(&mut self, latency: u128) {
316 self.last_checked_at = Some(Utc::now());
317 self.check_count += 1;
318 self.latency_ms = Some(latency);
319 }
320
321 /// Records a failed check of the proxy
322 pub fn record_check_failure(&mut self) {
323 self.last_checked_at = Some(Utc::now());
324 self.check_count += 1;
325 self.check_failure_count += 1;
326 }
327
328 /// Records a successful use of the proxy
329 pub fn record_use(&mut self) {
330 self.last_used_at = Some(Utc::now());
331 self.use_count += 1;
332 }
333
334 /// Records a failed use of the proxy
335 pub fn record_use_failure(&mut self) {
336 self.use_failure_count += 1;
337 }
338
339 /// Calculates the success rate of the proxy based on check history
340 #[must_use]
341 pub fn check_success_rate(&self) -> usize {
342 if self.check_count == 0 {
343 return 0;
344 }
345
346 let success_count = self.check_count - self.check_failure_count;
347 100 * success_count / self.check_count
348 }
349
350 /// Calculates the success rate of the proxy based on usage history
351 #[must_use]
352 pub fn use_success_rate(&self) -> usize {
353 if self.use_count == 0 {
354 return 0;
355 }
356
357 let success_count = self.use_count - self.use_failure_count;
358 100 * success_count / self.use_count
359 }
360
361 /// Returns a connection string representation of the proxy
362 #[must_use]
363 pub fn to_connection_string(&self) -> String {
364 let auth_part = match (&self.username, &self.password) {
365 (Some(u), Some(p)) => format!("{u}:{p}@"),
366 _ => String::new(),
367 };
368
369 format!(
370 "{}://{}{}:{}",
371 self.proxy_type.to_string().to_lowercase(),
372 auth_part,
373 self.address,
374 self.port
375 )
376 }
377
378 /// Updates the proxy with new information from a check
379 pub fn update_metadata(
380 &mut self,
381 country: Option<String>,
382 organization: Option<String>,
383 hostname: Option<String>,
384 anonymity: Option<AnonymityLevel>,
385 ) {
386 if let Some(c) = country {
387 self.country = Some(c);
388 }
389
390 if let Some(o) = organization {
391 self.organization = Some(o);
392 }
393
394 if let Some(h) = hostname {
395 self.hostname = Some(h);
396 }
397
398 if let Some(a) = anonymity {
399 self.anonymity = a;
400 }
401 }
402
403 /// Updates the proxy with network metadata from a sleuth lookup
404 pub fn update_with_ip_metadata(&mut self, metadata: IpMetadata) {
405 // Update the hostname if not already set
406 if self.hostname.is_none() {
407 if let Some(hostname_value) = &metadata.hostname {
408 self.hostname = Some(hostname_value.clone());
409 } else {
410 self.hostname = Some(String::new());
411 }
412 }
413
414 // Update CIDR information
415 if let Some(network) = &metadata.network {
416 if let Some(ref cidr_value) = network.cidr {
417 self.cidr = Some(cidr_value.clone());
418 }
419
420 // Update organization name
421 if let Some(org) = &network.organization {
422 if let Some(name) = &org.name {
423 self.organization = Some(String::new());
424 if let Some(org_name) = &mut self.organization {
425 org_name.clone_from(name);
426 }
427 }
428
429 // Update ASN
430 self.asn = Some(String::new());
431 if let Some(asn) = &mut self.asn {
432 if let Some(org_asn) = &org.asn {
433 asn.clone_from(org_asn);
434 }
435 }
436 }
437
438 // Update location-based information
439 if let Some(location) = &network.location {
440 if let Some(country) = &location.country {
441 self.country = Some(String::new());
442 if let Some(country_name) = &mut self.country {
443 country_name.clone_from(country);
444 }
445 }
446 }
447 }
448
449 // Store the full metadata structure
450 self.ip_metadata = Some(metadata);
451 }
452
453 /// Gets the full IP metadata if available
454 #[must_use]
455 pub fn get_ip_metadata(&self) -> Option<&IpMetadata> {
456 self.ip_metadata.as_ref()
457 }
458}
459
460/// Helper functions for serialization and deserialization
461impl Proxy {
462 /// Serializes the proxy to a JSON string
463 ///
464 /// # Errors
465 ///
466 /// This function will return an error if the serialization fails,
467 /// such as when the proxy contains data that cannot be represented in JSON.
468 pub fn to_json(&self) -> Result<String, serde_json::Error> {
469 serde_json::to_string(self)
470 }
471
472 /// Deserializes a proxy from a JSON string
473 ///
474 /// # Errors
475 ///
476 /// This function will return an error if the provided string is not valid JSON
477 /// or if it doesn't match the expected structure for a `Proxy` object.
478 pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
479 serde_json::from_str(json)
480 }
481}