Skip to main content

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}