# Rust reqwest 代理接入示例

使用 reqwest Proxy、账密认证、异步客户端和超时完成代理请求。

Rust 异步服务可以使用调用方项目自行锁定的 `reqwest` 和 Tokio。代理认证通过 `Proxy` 配置，不写入目标请求头。

## 前置条件

- 调用方项目自行安装并锁定 `reqwest` 与 Tokio。
- 通过受控环境注入 `PROXY_HOST`、`PROXY_PORT`、`PROXY_USERNAME` 和 `PROXY_PASSWORD`。
- 为服务设置并发上限和整体请求截止时间。

## 完整示例

```rust
use reqwest::{Client, Proxy};
use std::{env, error::Error, time::Duration};

/// 通过账密代理异步请求公开出口检测地址。
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let proxy_host = env::var("PROXY_HOST")?;
    let proxy_port = env::var("PROXY_PORT")?;
    let proxy_username = env::var("PROXY_USERNAME")?;
    let proxy_password = env::var("PROXY_PASSWORD")?;
    let proxy = Proxy::all(format!("http://{proxy_host}:{proxy_port}"))?
        .basic_auth(&proxy_username, &proxy_password);
    let client = Client::builder()
        .proxy(proxy)
        .connect_timeout(Duration::from_secs(10))
        .timeout(Duration::from_secs(30))
        .build()?;

    let body = client
        .get("https://<public-test-host>/ip")
        .send()
        .await?
        .error_for_status()?
        .text()
        .await?;
    println!("{body}");
    Ok(())
}
```

## 预期结果

- `Proxy::all` 将 HTTP 和 HTTPS 目标统一交给当前 HTTP 代理。
- 客户端复用连接，并分别限制连接阶段和整体请求时间。
- 成功状态返回代理出口检测正文。

## 错误处理

- 407 检查 `.basic_auth` 的代理账密与当前权益。
- 连接和请求超时分别用于定位网络建立阶段与目标响应阶段。
- 只对幂等请求执行带抖动的有限重试。

## 安全提示

不要使用 `Debug` 输出 `Proxy` 或客户端配置。错误上报只携带错误类别、任务标识和脱敏目标主机，不记录代理认证内容。

html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}
