Skip to main content

nurtex_proxy/
checker.rs

1use serde::Deserialize;
2use tokio::io::{AsyncReadExt, AsyncWriteExt};
3
4use crate::Proxy;
5use crate::result::ProxyResult;
6
7/// Структура информации об IP
8#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
9pub struct IpInfo {
10  pub ip: String,
11  pub hostname: String,
12  pub city: String,
13  pub region: String,
14  pub country: String,
15  pub loc: String,
16  pub org: String,
17  pub postal: String,
18  pub timezone: String,
19  pub readme: String,
20}
21
22/// Трейт прокси чекера
23pub trait ProxyChecker {
24  /// Метод проверки работоспособности прокси, используя `ipinfo.io`.
25  ///
26  /// ## Примеры
27  ///
28  /// ```rust, ignore
29  /// use nurtex_proxy::{Proxy, ProxyChecker};
30  ///
31  /// // Создаём прокси
32  /// let proxy = Proxy::new("PROXY_IP:PROXY_PORT");
33  ///
34  /// // Проверяем доступность прокси
35  /// if proxy.check_proxy().await {
36  ///   println!("Прокси доступен");
37  /// } else {
38  ///   println!("Прокси недоступен");
39  /// }
40  /// ```
41  fn check_proxy(&self) -> impl std::future::Future<Output = bool> + Send;
42
43  /// Метод получения информации об IP с `ipinfo.io`.
44  ///
45  /// ## Примеры
46  ///
47  /// ```rust, ignore
48  /// use nurtex_proxy::{Proxy, ProxyChecker};
49  ///
50  /// // Создаём прокси и получаем информацию об IP
51  /// let proxy = Proxy::new("PROXY_IP:PROXY_PORT");
52  /// let ip_info = proxy.get_ip_info().await;
53  ///
54  /// println!("Имя хоста: {}", ip_info.hostname);
55  /// println!("Страна: {}", ip_info.country);
56  /// println!("Город: {}", ip_info.city);
57  /// println!("Локация: {}", ip_info.loc);
58  /// ```
59  fn get_ip_info(&self) -> impl std::future::Future<Output = Option<IpInfo>> + Send;
60}
61
62impl ProxyChecker for Proxy {
63  async fn check_proxy(&self) -> bool {
64    if !self.is_available().await {
65      return false;
66    }
67
68    let ip_info = match self.get_ip_info().await {
69      Some(info) => info,
70      None => return false,
71    };
72
73    if let Some(ip) = self.get_ip() {
74      if ip != ip_info.ip {
75        return false;
76      }
77    }
78
79    true
80  }
81
82  async fn get_ip_info(&self) -> Option<IpInfo> {
83    let mut stream = match self.connect("ipinfo.io", 80).await {
84      ProxyResult::Ok(s) => s,
85      ProxyResult::Err(_) => return None,
86    };
87
88    let _ = stream.write_all(b"GET / HTTP/1.0\r\nHost: ipinfo.io\r\n\r\n").await;
89
90    let mut buf = Vec::new();
91    let _ = stream.read_to_end(&mut buf).await;
92
93    let data = match String::from_utf8(buf) {
94      Ok(s) => s,
95      Err(_) => String::new(),
96    };
97
98    let split_data = data.split("\n").collect::<Vec<&str>>();
99    let mut pretty_data = String::new();
100
101    // Здесь можно просто пропустить заголовки
102    for (i, item) in split_data.iter().enumerate() {
103      if i < 7 {
104        continue;
105      }
106
107      pretty_data.push_str(*item);
108    }
109
110    let ip_info: IpInfo = match serde_json::from_str(&pretty_data) {
111      Ok(info) => info,
112      Err(_) => return None,
113    };
114
115    Some(ip_info)
116  }
117}
118
119#[cfg(test)]
120mod tests {
121  use crate::{Proxy, ProxyChecker, ProxyType};
122
123  #[tokio::test]
124  async fn test_proxy_check() {
125    let proxy = Proxy::new("98.175.31.222:4145", ProxyType::Socks5);
126
127    if proxy.check_proxy().await {
128      println!("Доступен");
129    } else {
130      println!("Недоступен");
131    }
132  }
133
134  #[tokio::test]
135  async fn test_get_ip_info() {
136    let proxy = Proxy::new("98.175.31.222:4145", ProxyType::Socks5);
137    println!("Информация об IP: {:?}", proxy.get_ip_info().await);
138  }
139}