Перейти к содержимому
Гайды 7 мин чтения 2 просмотров

Использование прокси в Rust

Это руководство объясняет, как интегрировать прокси в Rust-приложения, используя библиотеки reqwest и hyper. Включает примеры кода и советы по работе с GProxy.

Использование прокси-серверов в Rust для HTTP-запросов эффективно реализуется с помощью библиотек reqwest для высокоуровневых операций и hyper для низкоуровневого контроля над сетевыми соединениями.

Прокси-серверы используются в Rust-приложениях для обхода географических ограничений, анонимизации запросов, сбора данных (веб-скрейпинга), распределения нагрузки и повышения безопасности. Выбор между reqwest и hyper зависит от требуемого уровня абстракции и контроля над сетевым стеком. reqwest предоставляет удобный API для большинства задач, в то время как hyper предлагает максимальную гибкость для создания кастомных решений.

Использование прокси с reqwest

reqwest — это популярный и высокоуровневый HTTP-клиент для Rust, построенный на базе hyper. Он упрощает выполнение HTTP-запросов и поддерживает различные типы прокси-серверов.

Конфигурация HTTP/HTTPS прокси

Для настройки HTTP или HTTPS прокси в reqwest используется метод proxy() у ClientBuilder. Прокси может быть указан для всех схем (all), только для HTTP (http) или только для HTTPS (https).

use reqwest::{Client, Error, Proxy};
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Error> {
    // Пример HTTP прокси
    let http_proxy_url = "http://user:password@proxy.example.com:8080";
    let client_http = Client::builder()
        .proxy(Proxy::http(http_proxy_url)?)
        .timeout(Duration::from_secs(10))
        .build()?;

    let res_http = client_http.get("http://httpbin.org/ip").send().await?;
    println!("HTTP Proxy Response: {:?}", res_http.text().await?);

    // Пример HTTPS прокси
    let https_proxy_url = "https://user:password@secure-proxy.example.com:8443";
    let client_https = Client::builder()
        .proxy(Proxy::https(https_proxy_url)?)
        .timeout(Duration::from_secs(10))
        .build()?;

    let res_https = client_https.get("https://httpbin.org/ip").send().await?;
    println!("HTTPS Proxy Response: {:?}", res_https.text().await?);

    // Пример прокси для всех схем
    let all_proxy_url = "http://user:password@all-proxy.example.com:3128";
    let client_all = Client::builder()
        .proxy(Proxy::all(all_proxy_url)?)
        .timeout(Duration::from_secs(10))
        .build()?;

    let res_all = client_all.get("http://httpbin.org/ip").send().await?;
    println!("All Proxy HTTP Response: {:?}", res_all.text().await?);

    let res_all_https = client_all.get("https://httpbin.org/ip").send().await?;
    println!("All Proxy HTTPS Response: {:?}", res_all_https.text().await?);

    Ok(())
}

Конфигурация SOCKS прокси

reqwest также поддерживает SOCKS5 прокси. Для этого используется метод socks5() у Proxy.

use reqwest::{Client, Error, Proxy};
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Error> {
    let socks5_proxy_url = "socks5://user:password@socks.example.com:1080";
    let client_socks = Client::builder()
        .proxy(Proxy::socks5(socks5_proxy_url)?)
        .timeout(Duration::from_secs(10))
        .build()?;

    let res_socks = client_socks.get("http://httpbin.org/ip").send().await?;
    println!("SOCKS5 Proxy Response: {:?}", res_socks.text().await?);

    Ok(())
}

Аутентификация прокси

Аутентификация для прокси-сервера включается непосредственно в URL прокси в формате схема://пользователь:пароль@хост:порт. reqwest автоматически обрабатывает эти учетные данные.

// Пример с аутентификацией
let proxy_with_auth = "http://myuser:mypassword@myproxy.com:8080";
let client = Client::builder()
    .proxy(Proxy::http(proxy_with_auth)?)
    .build()?;

Прокси через переменные окружения

reqwest может автоматически использовать прокси-серверы, указанные в переменных окружения. Это удобно для развертывания приложений без изменения кода.

  • HTTP_PROXY или http_proxy: для HTTP-запросов.
  • HTTPS_PROXY или https_proxy: для HTTPS-запросов.
  • ALL_PROXY или all_proxy: для всех запросов, если не указаны более специфичные.
  • NO_PROXY или no_proxy: список хостов, для которых прокси не используется (разделенных запятыми).

Если прокси настроен через ClientBuilder::proxy() и одновременно через переменные окружения, явная конфигурация через ClientBuilder имеет приоритет.

Ротация прокси

reqwest не имеет встроенного механизма ротации прокси. Для реализации ротации необходимо:
1. Создать список прокси-серверов.
2. Разработать логику выбора прокси из списка (например, случайный выбор, Round Robin).
3. Создавать новый reqwest::Client с выбранным прокси для каждого запроса или группы запросов.

Пример примитивной ротации:

use reqwest::{Client, Error, Proxy};
use std::time::Duration;
use rand::seq::SliceRandom; // Добавьте в Cargo.toml: rand = "0.8"

#[tokio::main]
async fn main() -> Result<(), Error> {
    let proxies = vec![
        "http://proxy1.example.com:8080",
        "http://proxy2.example.com:8080",
        "http://proxy3.example.com:8080",
    ];

    let mut rng = rand::thread_rng();
    let selected_proxy_url = proxies.choose(&mut rng).ok_or_else(|| Error::from(std::io::Error::new(std::io::ErrorKind::Other, "No proxies available")))?;

    let client = Client::builder()
        .proxy(Proxy::http(selected_proxy_url)?)
        .timeout(Duration::from_secs(10))
        .build()?;

    let res = client.get("http://httpbin.org/ip").send().await?;
    println!("Rotated Proxy Response: {:?}", res.text().await?);

    Ok(())
}

Использование прокси с hyper

hyper — это низкоуровневая библиотека для работы с HTTP, предоставляющая примитивы для построения клиентов и серверов. Работа с прокси через hyper требует более глубокого понимания сетевых протоколов и ручной реализации.

HTTP/HTTPS прокси

Для HTTP/HTTPS прокси с hyper требуется создание кастомного коннектора. Для HTTP-прокси это означает установление прямого TCP-соединения с прокси-сервером и отправку обычного HTTP-запроса. Для HTTPS-прокси требуется сначала установить TCP-соединение с прокси, затем отправить команду CONNECT, дождаться ответа 200 OK, а затем уже устанавливать TLS-соединение с целевым сервером через прокси.

Пример для HTTPS-прокси (упрощенный, без полноценной обработки ошибок и аутентификации):

use hyper::{Client, Uri};
use hyper::client::conn::Builder;
use hyper_tls::HttpsConnector;
use tokio::net::TcpStream;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use std::error::Error;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
    let proxy_addr = "proxy.example.com:8080"; // Замените на адрес вашего HTTP/HTTPS прокси
    let target_uri: Uri = "https://httpbin.org/ip".parse()?;

    // 1. Устанавливаем TCP-соединение с прокси-сервером
    let mut proxy_stream = TcpStream::connect(proxy_addr).await?;

    // 2. Отправляем команду CONNECT для HTTPS
    let connect_request = format!(
        "CONNECT {}:443 HTTP/1.1\r\nHost: {}\r\n\r\n",
        target_uri.host().unwrap(),
        target_uri.host().unwrap()
    );
    proxy_stream.write_all(connect_request.as_bytes()).await?;

    // 3. Читаем ответ от прокси
    let mut buffer = [0; 1024];
    let n = proxy_stream.read(&mut buffer).await?;
    let response = String::from_utf8_lossy(&buffer[..n]);

    if !response.contains("200 Connection established") {
        return Err(format!("Proxy CONNECT failed: {}", response).into());
    }
    println!("Proxy CONNECT successful.");

    // 4. Устанавливаем TLS-соединение через прокси
    let connector = HttpsConnector::new();
    let tls_stream = connector.connect(target_uri.clone(), proxy_stream).await?;

    // 5. Создаем HTTP-клиент с использованием TLS-потока
    let (mut request_sender, connection) = Builder::new()
        .http1_pipeline_flush(true)
        .handshake(tls_stream)
        .await?;

    tokio::spawn(async move {
        if let Err(e) = connection.await {
            eprintln!("Error in connection: {}", e);
        }
    });

    // 6. Отправляем запрос через установленное соединение
    let request = hyper::Request::builder()
        .uri(target_uri)
        .header(hyper::header::HOST, "httpbin.org")
        .body(hyper::Body::empty())?;

    let response = request_sender.send_request(request).await?;

    // 7. Читаем ответ
    let body_bytes = hyper::body::to_bytes(response.into_body()).await?;
    println!("Hyper Proxy Response: {}", String::from_utf8_lossy(&body_bytes));

    Ok(())
}

SOCKS прокси

Для SOCKS прокси с hyper требуется использовать сторонние крейты, такие как tokio-socks, для установления соединения через SOCKS-сервер. Этот крейт позволяет создать TcpStream уже проксированным через SOCKS, который затем может быть использован hyper.

use hyper::{Client, Uri};
use hyper_tls::HttpsConnector;
use tokio_socks::Socks5Stream;
use tokio::net::TcpStream;
use std::error::Error;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
    let socks_proxy_addr = "127.0.0.1:1080"; // Замените на адрес вашего SOCKS5 прокси
    let target_uri: Uri = "https://httpbin.org/ip".parse()?;
    let target_host = target_uri.host().unwrap();
    let target_port = target_uri.port_u16().unwrap_or(443);

    // 1. Устанавливаем TCP-соединение через SOCKS5 прокси
    let socks_stream = Socks5Stream::connect(
        socks_proxy_addr,
        (target_host, target_port),
        None, // None для без аутентификации, Some((user, pass)) для аутентификации
    ).await?;

    // 2. Преобразуем Socks5Stream в TcpStream для HttpsConnector
    let tcp_stream = socks_stream.into_inner();

    // 3. Создаем HttpsConnector и используем наш проксированный TcpStream
    let https_connector = HttpsConnector::new();
    let tls_stream = https_connector.connect(target_uri.clone(), tcp_stream).await?;

    // 4. Создаем hyper client
    let client = Client::builder().build::<_, hyper::Body>(hyper::client::HttpConnector::new());

    // NOTE: Для использования custom stream с hyper.client::Client,
    // необходимо создать Service, который будет возвращать наш tls_stream.
    // Пример выше для hyper::client::conn::Builder более показателен для низкоуровневого контроля.
    // Если нужно использовать Client::builder().build(), то нужно реализовать кастомный `hyper::service::Service`
    // для `MakeConnection`, который будет использовать `tokio-socks`.
    // Это значительно усложняет пример.

    // Для демонстрации, мы можем использовать наш tls_stream напрямую с низкоуровневым API hyper
    let (mut request_sender, connection) = hyper::client::conn::Builder::new()
        .http1_pipeline_flush(true)
        .handshake(tls_stream)
        .await?;

    tokio::spawn(async move {
        if let Err(e) = connection.await {
            eprintln!("Error in connection: {}", e);
        }
    });

    let request = hyper::Request::builder()
        .uri(target_uri)
        .header(hyper::header::HOST, target_host)
        .body(hyper::Body::empty())?;

    let response = request_sender.send_request(request).await?;

    let body_bytes = hyper::body::to_bytes(response.into_body()).await?;
    println!("Hyper SOCKS5 Proxy Response: {}", String::from_utf8_lossy(&body_bytes));

    Ok(())
}

Примечание: Использование hyper::client::Client с кастомными потоками, как Socks5Stream, требует реализации hyper::service::Service для MakeConnection, что значительно усложняет код и выходит за рамки краткого примера. Показанный выше подход с hyper::client::conn::Builder демонстрирует низкоуровневый контроль.

Сравнение reqwest и hyper для прокси

Аспект reqwest hyper
Уровень абстракции Высокий Низкий
Простота прокси Простая конфигурация через ClientBuilder::proxy() Требует ручной реализации коннектора или использования сторонних крейтов
HTTP/S прокси Встроенная поддержка, автоматическая обработка Требует ручной обработки CONNECT для HTTPS, создания TcpStream к прокси
SOCKS прокси Встроенная поддержка, автоматическая обработка Требует сторонних крейтов (например, tokio-socks) для создания потока
Гибкость Меньше, подходит для большинства стандартных случаев Высокая, подходит для кастомных решений, специфических протоколов, высокопроизводительных серверов
Код для прокси Минимальный Значительный, требует понимания сетевых протоколов
Производительность Высокая, так как базируется на hyper Максимально возможная, с полным контролем над соединениями

Рекомендации и лучшие практики

Обработка ошибок

Всегда обрабатывайте Result типы, возвращаемые функциями reqwest и hyper. Используйте оператор ? для упрощения кода, но убедитесь, что ошибки логируются или обрабатываются соответствующим образом.

Тайм-ауты

Настройка тайм-аутов критически важна для стабильности приложений, использующих прокси. Прокси-серверы могут быть медленными или недоступными.
* reqwest: Используйте ClientBuilder::timeout(Duration::from_secs(10)) для общего тайм-аута или connect_timeout для тайм-аута соединения.
* hyper: Тайм-ауты на уровне TCP (например, TcpStream::set_nodelay) или на уровне TLS (через настройки rustls/native-tls) должны быть настроены вручную. Для операций send_request или to_bytes можно использовать tokio::time::timeout.

Пулинг соединений

reqwest::Client по умолчанию использует пулинг соединений, что повышает производительность за счет переиспользования уже установленных TCP-соединений. Для hyper, если вы используете hyper::client::Client, пулинг также присутствует. При ручном управлении соединениями через hyper::client::conn::Builder пулинг должен быть реализован самостоятельно.

Безопасность

  • Используйте только доверенные прокси-серверы, особенно при работе с конфиденциальными данными.
  • Применяйте аутентификацию для прокси, если она поддерживается.
  • Избегайте передачи учетных данных прокси через открытые логи или незащищенные каналы.

Управление ресурсами

Создавайте reqwest::Client или hyper::Client один раз и переиспользуйте его для нескольких запросов. Это позволяет эффективно использовать пулинг соединений и другие внутренние оптимизации. Создание нового клиента для каждого запроса приводит к избыточным накладным расходам.

Обновлено: 03.03.2026
Назад к категории

Попробуйте наши прокси

20,000+ прокси в 100+ странах мира