在 Rust 中使用 reqwest 配置代理,就是给 ClientBuilder 设置代理参数;而 hyper 作为更底层的 HTTP 库,需要手动通过代理服务器建立连接。
代理服务器充当网络请求的中间人,可带来匿名性、访问受地域限制的内容、负载均衡和流量过滤等好处。Rust 的异步 HTTP 客户端 reqwest 和 hyper 集成代理功能的方式不同:reqwest 提供高层的内置支持,而 hyper 要求对连接过程进行更细粒度的控制。
使用 reqwest 配置代理
reqwest 是构建在 hyper 之上、广受欢迎且易于上手的 Rust HTTP 客户端。它简化了常见的 HTTP 任务,包括代理配置。reqwest 的 ClientBuilder 提供了设置各类代理的方法。
代理配置
要在 reqwest 中使用代理,请通过 reqwest::ClientBuilder 及其 proxy() 方法创建 reqwest::Client 实例。proxy() 方法接受一个 reqwest::Proxy 对象,该对象可针对不同的代理协议构造:
- HTTP 代理:使用
Proxy::http(url)配置,用于普通 HTTP 请求。 - HTTPS 代理(CONNECT 方法):使用
Proxy::https(url)配置,用于 HTTPS 请求。reqwest通过CONNECT方法在代理上隧道传输 TLS 连接。 - SOCKS5 代理:使用
Proxy::socks5(url)配置,用于 SOCKS5 代理服务器。 - 统一代理:
Proxy::all(url)为 HTTP 和 HTTPS 请求配置同一个代理。
代理认证
许多代理服务器要求认证。reqwest::Proxy 通过 basic_auth() 方法支持基本认证。
环境变量支持
默认情况下,reqwest 的 ClientBuilder 会自动检查 HTTP_PROXY、HTTPS_PROXY 和 NO_PROXY 环境变量。若已设置这些变量,除非通过 ClientBuilder::no_proxy() 或显式配置具体代理来覆盖,reqwest 都会使用它们。
示例:reqwest 代理用法
本示例演示如何配置 reqwest 使用 HTTP、HTTPS 和 SOCKS5 代理,并包含基本认证。
use reqwest::{Client, Error, Proxy};
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Error> {
// 1. HTTP 代理
let http_proxy_url = "http://user:[email protected]:8080";
let http_client = Client::builder()
.proxy(Proxy::http(http_proxy_url)?)
.timeout(Duration::from_secs(10))
.build()?;
println!("HTTP Proxy request...");
let http_res = http_client.get("http://httpbin.org/ip").send().await?;
println!("HTTP Proxy Response: {:?}", http_res.text().await?);
// 2. HTTPS 代理(CONNECT 方法)
// 注意:对于 HTTPS,代理 URL 本身可能是 HTTP,但它会隧道传输 HTTPS 流量。
let https_proxy_url = "http://user:[email protected]:8080";
let https_client = Client::builder()
.proxy(Proxy::https(https_proxy_url)?)
.timeout(Duration::from_secs(10))
.build()?;
println!("\nHTTPS Proxy request...");
let https_res = https_client.get("https://httpbin.org/ip").send().await?;
println!("HTTPS Proxy Response: {:?}", https_res.text().await?);
// 3. SOCKS5 代理
let socks5_proxy_url = "socks5://user:[email protected]:1080";
let socks5_client = Client::builder()
.proxy(Proxy::socks5(socks5_proxy_url)?)
.timeout(Duration::from_secs(10))
.build()?;
println!("\nSOCKS5 Proxy request...");
let socks5_res = socks5_client.get("http://httpbin.org/ip").send().await?;
println!("SOCKS5 Proxy Response: {:?}", socks5_res.text().await?);
// 4. 统一代理(同时适用于 HTTP 和 HTTPS 请求)
let all_proxy_url = "http://user:[email protected]:8080";
let all_client = Client::builder()
.proxy(Proxy::all(all_proxy_url)?)
.timeout(Duration::from_secs(10))
.build()?;
println!("\nUnified Proxy (HTTP) request...");
let all_http_res = all_client.get("http://httpbin.org/ip").send().await?;
println!("Unified Proxy (HTTP) Response: {:?}", all_http_res.text().await?);
println!("\nUnified Proxy (HTTPS) request...");
let all_https_res = all_client.get("https://httpbin.org/ip").send().await?;
println!("Unified Proxy (HTTPS) Response: {:?}", all_https_res.text().await?);
Ok(())
}
注意:请把 your-http-proxy.com:8080、your-https-proxy.com:8080 和 your-socks5-proxy.com:1080 替换为实际的代理服务器地址和端口;需要认证的代理还要包含 user:password。
使用 hyper 配置代理
hyper 是一个底层、高性能的 HTTP 库,是包括 reqwest 在内的许多 Rust HTTP crate 的基础。与 reqwest 不同,hyper 没有为其客户端提供内置的、直接的代理配置方法。在 hyper 中使用代理需要手动管理底层 TCP 连接,或集成专门的代理 crate。
手动管理连接
要在 hyper 中使用代理,必须先建立到代理服务器的连接,然后让 hyper 使用这个已建立的连接,或使用处理代理逻辑的自定义 connector。
HTTP 请求走 HTTP 代理
对于经由 HTTP 代理的明文 HTTP 请求,客户端先与代理服务器建立 TCP 连接,然后直接向代理发送 HTTP 请求,并在请求行中写出完整的目标 URL(例如 GET http://target.com/path HTTP/1.1)。Host 请求头仍应指向目标服务器。
HTTPS 请求走 HTTP 代理(CONNECT 方法)
对于 HTTPS 请求,客户端首先向代理发送 HTTP CONNECT 请求,要求其建立到目标主机和端口的 TCP 隧道。代理返回 200 OK 后,客户端便通过该隧道直接与目标服务器完成 TLS 握手。
SOCKS5 代理
SOCKS5 代理运行在比 HTTP 代理更低的层级,支持包括 TCP 和 UDP 在内的多种协议。将 SOCKS5 与 hyper 集成通常需要借助专门的 SOCKS5 客户端库(例如 tokio-socks)建立代理连接,再把该连接交给 hyper 的 connection builder。
示例:hyper 代理用法(HTTP 请求走 HTTP 代理)
本示例演示如何用 hyper 手动构造请求并管理 TCP 流,从而通过 HTTP 代理发起一个基础 HTTP 请求。它展示的是底层机制;若要做成健壮的方案,建议封装这段逻辑或使用专门的 connector。
use hyper::{Body, Client, Request, Uri};
use hyper::client::conn::Builder;
use tokio::net::TcpStream;
use std::str::FromStr;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let proxy_addr = "127.0.0.1:8080"; // 替换为您的 HTTP 代理地址
let target_url = "http://httpbin.org/ip";
println!("Connecting to proxy: {}", proxy_addr);
let stream = TcpStream::connect(proxy_addr).await?;
// 对于 HTTP 代理,我们连接到代理并在请求中发送完整 URI。
// 纯 HTTP 目标不需要 CONNECT 方法。
let (mut sender, conn) = Builder::new()
.http1_only(true) // 确保使用 HTTP/1.1 以兼容 HTTP 代理
.handshake(stream)
.await?;
tokio::spawn(async move {
if let Err(e) = conn.await {
eprintln!("Connection error: {:?}", e);
}
});
let req = Request::builder()
.uri(Uri::from_str(target_url)?) // 用于 HTTP 代理转发的绝对 URI
.header("Host", "httpbin.org") // 目标服务器的 Host 请求头
.body(Body::empty())?;
println!("Sending request through proxy to: {}", target_url);
let res = sender.send(req).await?;
println!("Response Status: {}", res.status());
let body_bytes = hyper::body::to_bytes(res.into_body()).await?;
println!("Response Body: {}", String::from_utf8_lossy(&body_bytes));
Ok(())
}
注意:本示例假设代理是一个无需认证、直接转发 HTTP 请求的基础 HTTP 代理。对于 HTTPS 或需要认证的代理,逻辑会复杂得多,需要 CONNECT 请求或专门的代理协商库。
为 hyper 使用外部 connector
在 hyper 中处理更复杂的代理场景时,尤其是 SOCKS5 或需要认证的 HTTP/HTTPS 代理,常见做法是实现自定义的 hyper::client::connect::Connection,或使用已提供该实现的 crate。例如对于 SOCKS5,通常用 tokio-socks 之类的 crate 建立经 SOCKS5 代理的 TcpStream,再用该流调用 hyper::client::conn::Builder::handshake。
代理场景下 reqwest 与 hyper 的对比
| 特性 | reqwest |
hyper |
|---|---|---|
| 代理配置 | 内置的高层方法(ClientBuilder::proxy()) |
手动实现 hyper::client::connect::Connection 或集成外部库 |
| 易用性 | 高层封装,常见代理类型使用简单 | 底层实现,代理场景较复杂,样板代码更多 |
| 支持的代理类型 | HTTP、HTTPS(CONNECT)、SOCKS5 | 每种类型都需手动处理;SOCKS5 或复杂认证通常依赖外部 crate |
| 认证 | 内置(Proxy::basic_auth()) |
手动注入请求头(Proxy-Authorization)或使用特定代理库的功能 |
| 控制粒度 | 中等,屏蔽了连接细节 | 高,可精细控制连接与 HTTP 协议细节 |
| 依赖 | 代理场景更简单,reqwest 处理底层复杂度 |
要实现健壮的代理逻辑可能需要额外 crate(tokio-socks、自定义 connector) |
| 典型使用场景 | 大多数需要带代理支持的 HTTP 请求、看重开发速度和简洁性的应用 | 构建自定义 HTTP 客户端、服务端或高度优化的网络组件,需要对网络连接做精细控制 |
最佳实践与注意事项
错误处理
代理连接可能因多种原因失败(网络问题、代理宕机、认证失败)。请为与代理相关的操作实现健壮的错误处理,尤其是在用 hyper 手动管理连接时。reqwest 返回 reqwest::Error,可从中检查代理相关的问题。
代理类型与协议兼容性
- HTTP 代理:主要用于 HTTP 流量,可通过
CONNECT方法隧道传输 HTTPS 流量。 - SOCKS5 代理:与协议无关,支持 TCP 和 UDP。在处理非 HTTP 流量或需要更高匿名度时通常更受青睐。
- 请确保所配置的代理类型与代理服务器的能力以及您要发送的流量类型相匹配。
认证
对需要认证的代理,务必提供正确的凭据。认证配置错误是代理连接故障的常见原因。在 reqwest 中使用 Proxy::basic_auth();在 hyper 或自定义 connector 中,请发送带有相应凭据的 Proxy-Authorization 请求头(例如 Base64 编码的 username:password)。
性能影响
使用代理会在网络路径中增加一跳,可能提高延迟。性能开销取决于代理服务器的位置、负载,以及客户端、代理与目标服务器之间的网络状况。
安全注意事项
- 信任:只使用可信的代理服务器。恶意代理可能查看、篡改或记录您的流量。
- HTTPS 代理:使用带
CONNECT方法的 HTTPS 代理时,代理只建立隧道,您的客户端直接与目标服务器完成 TLS 握手。这意味着代理无法解密您的 HTTPS 流量内容(除非它是在您客户端上安装了自有根证书的"中间人"代理)。reqwest默认会安全地处理这一点。 - 代理请求头:代理常会添加
X-Forwarded-For之类的请求头。如果匿名性是首要考虑,请留意这些字段。
与环境变量的交互
reqwest 会自动遵循 HTTP_PROXY、HTTPS_PROXY 和 NO_PROXY 环境变量。如需覆盖该行为,请使用 ClientBuilder::no_proxy() 或显式配置代理。直接使用 hyper 时,如有需要,您必须自行解析并应用这些环境变量。
