zeloxy 0.4.0

A library for creating lightweight, async, and lag-free proxy connections.
Documentation
use base64::Engine;
use base64::engine::general_purpose::STANDARD;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
use tokio_rustls::client::TlsStream;

use crate::{ErrorKind, ProxyAuth, ProxyError, ProxyResult};

/// Функция валидации ответа прокси
fn validate_proxy_response(resp: &[u8]) -> ProxyResult<()> {
  let resp_str = String::from_utf8_lossy(resp);
  let status_line = resp_str.lines().next().unwrap_or("");

  if !status_line.contains("200") && !status_line.contains("Connection established") {
    return Err(ProxyError::new(
      ErrorKind::NotConnected,
      format!("unsuccessful proxy response status: {}", status_line),
    ));
  }

  Ok(())
}

/// Функция создания подключения с HTTPS-прокси
pub async fn connect_https(
  stream: &mut TlsStream<TcpStream>,
  target_host: String,
  target_port: u16,
  auth: &Option<ProxyAuth>,
) -> ProxyResult<()> {
  let mut req = format!(
    "CONNECT {}:{} HTTP/1.1\r\nHost: {}:{}\r\n",
    target_host, target_port, target_host, target_port
  );

  if let Some(auth) = auth {
    let credentials = format!("{}:{}", auth.username(), auth.password());
    let encoded = STANDARD.encode(&credentials);

    req.push_str(&format!("Proxy-Authorization: Basic {}\r\n", encoded));
  }

  req.push_str("\r\n");

  stream.write_all(req.as_bytes()).await?;

  let mut resp = vec![0; 8192];

  let n = match tokio::time::timeout(std::time::Duration::from_secs(14), stream.read(&mut resp)).await {
    Ok(n) => n?,
    Err(_) => return Err(ProxyError::new(ErrorKind::Timeout, "failed to read buffer from stream")),
  };

  resp.truncate(n);

  validate_proxy_response(&resp)
}