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    self.bind("ipinfo.io".to_string(), 80);
84
85    let mut stream = match self.connect().await {
86      ProxyResult::Ok(s) => s,
87      ProxyResult::Err(_) => return None,
88    };
89
90    let _ = stream.write_all(b"GET / HTTP/1.0\r\nHost: ipinfo.io\r\n\r\n").await;
91
92    let mut buf = Vec::new();
93    let _ = stream.read_to_end(&mut buf).await;
94
95    let data = match String::from_utf8(buf) {
96      Ok(s) => s,
97      Err(_) => String::new(),
98    };
99
100    let split_data = data.split("\n").collect::<Vec<&str>>();
101    let mut pretty_data = String::new();
102
103    // Здесь можно просто пропустить заголовки
104    for (i, item) in split_data.iter().enumerate() {
105      if i < 7 {
106        continue;
107      }
108
109      pretty_data.push_str(*item);
110    }
111
112    let ip_info: IpInfo = match serde_json::from_str(&pretty_data) {
113      Ok(info) => info,
114      Err(_) => return None,
115    };
116
117    Some(ip_info)
118  }
119}
120
121#[cfg(test)]
122mod tests {
123  use crate::{Proxy, ProxyChecker, ProxyType};
124
125  #[tokio::test]
126  async fn test_proxy_check() {
127    let proxy = Proxy::new("98.175.31.222:4145", ProxyType::Socks5);
128
129    if proxy.check_proxy().await {
130      println!("Доступен");
131    } else {
132      println!("Недоступен");
133    }
134  }
135
136  #[tokio::test]
137  async fn test_get_ip_info() {
138    let proxy = Proxy::new("98.175.31.222:4145", ProxyType::Socks5);
139    println!("Информация об IP: {:?}", proxy.get_ip_info().await);
140  }
141}