ureq:Rust中一个简单、安全的、阻塞 I/O的HTTP客户端


Ureq 的首要任务是易于使用。对于任何想要一个低开销的 HTTP 客户端来完成工作的人来说,它非常有用。与 HTTP API 配合得很好。其功能包括 cookie、JSON、HTTP 代理、HTTPS、http与 crate 的互操作性以及字符集解码。

为了安全性和易于理解,Ureq 采用纯 Rust 语言。它避免了 unsafe直接使用。它使用阻塞 I/O而不是异步 I/O,因为这样可以保持 API 简单并将依赖关系降至最低。对于 TLS,ureq 使用 rustls 或 native-tls。

Ureq 使用阻塞 I/O,而不是 Rust 较新的异步 (async) I/O。异步 I/O 允许服务许多并发请求,而不会增加内存和操作系统线程的成本。但这是以复杂性为代价的。异步程序需要引入运行时(通常 是 async-std或tokio)。他们还需要任何可能阻塞的方法的异步变体,以及 任何可能调用另一个可能阻塞的方法的方法的异步变体。这意味着异步程序通常有很多依赖项 - 这会增加编译时间并增加风险。

如果您正在编写一个必须以最小的开销为许多客户端提供服务的 HTTP 服务器,那么异步的成本是值得的。然而,对于 HTTP客户端,我们认为这种成本通常是不值得付出的。异步 I/O 的低成本替代方案是阻塞 I/O,它具有不同的价格:每个并发请求需要一个操作系统线程。然而,这个代价通常并不高:大多数 HTTP 客户端都是按顺序发出请求,或者并发性较低。

这就是 ureq 使用阻塞 I/O 并计划保持这种方式的原因。其他 HTTP 客户端同时提供异步 API 和阻塞 API,但我们希望提供阻塞 API,而不需要引入异步 API 所需的所有依赖项。

最简单的形式,ureq 看起来像这样:

fn main() -> Result<(), ureq::Error> {
    let body: String = ureq::get("http://example.com")
        .set(
"Example-Header", "header value")
        .call()?
        .into_string()?;
    Ok(())
}

对于更多涉及的任务,您需要创建一个Agent。代理拥有一个连接池以供重用,如果您使用“cookies”功能,则还拥有一个 cookie 存储区。由于存在内部 Arc,并且 Agent 的所有克隆彼此共享状态,因此可以廉价地克隆 Agent。创建代理还允许设置 TLS 配置等选项。

  use ureq::{Agent, AgentBuilder};
  use std::time::Duration;

  let agent: Agent = ureq::AgentBuilder::new()
      .timeout_read(Duration::from_secs(5))
      .timeout_write(Duration::from_secs(5))
      .build();
  let body: String = agent.get("http://example.com/page")
      .call()?
      .into_string()?;

 
// Reuses the connection from previous request.
  let response: String = agent.put(
"http://example.com/upload")
      .set(
"Authorization", "example-token")
      .call()?
      .into_string()?;


如果启用“json”功能,Ureq 支持发送和接收 json:

  // Requires the `json` feature enabled.
  let resp: String = ureq::post(
"http://myapi.example.com/ingest")
      .set(
"X-My-Header", "Secret")
      .send_json(ureq::json!({
         
"name": "martin",
         
"rust": true
      }))?
      .into_string()?;


ureq 通过返回错误Result<T, ureq::Error>。其中包括 I/O 错误、协议错误和状态代码错误(当服务器响应 4xx 或 5xx 时)

use ureq::Error;

match ureq::get("http://mypage.example.com/").call() {
    Ok(response) => {
/* it worked */},
    Err(Error::Status(code, response)) => {
       
/* the server returned an unexpected status
           code (such as 400, 500 etc) */

    }
    Err(_) => {
/* some kind of io/transport error */ }
}

使用 SOCKS5 的示例

fn proxy_example_2() -> std::result::Result<(), ureq::Error> {
    // Configure a SOCKS proxy.
    let proxy = ureq::Proxy::new(
"socks5://user:password@cool.proxy:9090")?;
    let agent = ureq::AgentBuilder::new()
        .proxy(proxy)
        .build();

   
// This is proxied.
    let resp = agent.get(
"http://cool.server").call()?;
    Ok(())
}

HTTPS/TLS/SSL
在支持 rustls 的平台上,ureq 使用 rustls。在其他平台上,可以使用 [ AgentBuilder::tls_connector] 手动配置 native-tls。

如果您需要与仅支持安全性较低的 TLS 配置的服务器进行互操作(例如,rustls 不支持 TLS 1.0 和 1.1),您可能需要使用 native-tls。如果您需要验证 IP 地址的证书(目前 rustls 不支持),您可能还想使用它。

下面是构建使用 native-tls 的 Agent 的示例。它需要启用“native-tls”功能。

  use std::sync::Arc;
  use ureq::Agent;

  let agent = ureq::AgentBuilder::new()
      .tls_connector(Arc::new(native_tls::TlsConnector::new()?))
      .build();


点击标题