83个版本 (40个稳定版本)

2.10.1 2024年8月8日
2.10.0 2024年7月6日
2.9.7 2024年4月25日
2.9.6 2024年2月16日
0.4.5 2018年7月15日

#1 in HTTP客户端

Download history 297777/week @ 2024-05-03 329498/week @ 2024-05-10 344606/week @ 2024-05-17 332937/week @ 2024-05-24 365967/week @ 2024-05-31 358396/week @ 2024-06-07 349389/week @ 2024-06-14 366908/week @ 2024-06-21 346930/week @ 2024-06-28 368229/week @ 2024-07-05 353149/week @ 2024-07-12 402744/week @ 2024-07-19 381212/week @ 2024-07-26 388711/week @ 2024-08-02 413799/week @ 2024-08-09 312118/week @ 2024-08-16

1,568,594 每月下载量
1,634 个crate中 使用(831个直接使用)

MIT/Apache

355KB
6.5K SLoC

ureq

一个简单、安全的HTTP客户端。

Ureq的首要任务是易于使用。非常适合只需要完成任务的低开销HTTP客户端。与HTTP API配合得很好。其功能包括cookies、JSON、HTTP代理、HTTPS、与http crate的互操作性和字符集解码。

Ureq是用纯Rust编写的,以确保安全和易于理解。它避免了直接使用unsafe。它使用阻塞I/O而不是异步I/O,因为这可以使API简单,并保持依赖性最小。对于TLS,ureq使用rustls或native-tls

有关最近发布版本的详细信息,请参阅变更日志

使用方法

在其最简单的形式中,ureq看起来是这样的

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

对于更复杂的任务,您可能需要创建一个Agent。Agent保留一个连接池以供重用,如果您使用“cookies”功能,则还包括cookie存储。由于内部Arc,Agent可以廉价地克隆,并且所有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()?;

Ureq支持发送和接收JSON,如果您启用“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 */ }
}

有关Error类型的更多详细信息。

功能

为了启用最小依赖关系树,一些功能默认关闭。您可以在将ureq作为依赖项包含时控制它们。

ureq= {版本= "*",功能= ["json", "字符集"] }

  • tls 启用 https。默认情况下已启用。
  • native-certs 使默认 TLS 实现使用操作系统的信任存储(见下面的 TLS 文档)。
  • cookies 启用 cookies。
  • json 通过 serde_json 启用 Response::into_json()Request::send_json()
  • charset 启用解析 Content-Type 头的字符集部分(例如 Content-Type: text/plain; charset=iso-8859-1)。如果没有此选项,库默认使用 Rust 内置的 utf-8
  • socks-proxy 启用使用 socks4://socks4a://socks5://socks://(等于 socks5://)前缀的代理配置。
  • native-tls 启用一个适配器,您可以将其 native_tls::TlsConnector 实例传递给 AgentBuilder::tls_connector。由于存在意外切换到不受欢迎的 TLS 实现的风险,native-tls 从不被选中作为默认选项或由 crate 级便利调用(如 ureq::get 等)使用 – 它必须在代理上配置。对于 native-tlsnative-certs 功能不起作用。
  • gzip 启用请求 gzip 压缩的响应并对其进行解压缩。默认情况下已启用。
  • brotli 启用请求 brotli 压缩的响应并对其进行解压缩。
  • http-interop 启用从 http::Responsehttp::request::Builder(v0.2)到和从的转换方法。
  • http 启用从 http::Responsehttp::request::Builder(v1.0)到和从的转换方法。

普通请求

大多数标准方法(GET、POST、PUT 等)作为库顶部的函数支持(get()post()put() 等)。

这些顶层 http 方法函数创建一个 Request 实例,它遵循构建模式。使用

  • .call() 无请求体完成构建器。
  • .send() 请求体作为 Read(支持非已知大小的读取器的分块编码)。
  • .send_string() 请求体作为字符串。
  • .send_bytes() 将正文作为字节发送。
  • .send_form() 将键值对作为 application/x-www-form-urlencoded 发送。

JSON

通过启用 ureq = { version = "*", features = ["json"] } 功能,库支持 serde json。

内容长度和传输编码

在正文大小已知的请求中,库会发送 Content-Length 头部,换句话说,那些使用 .send_string().send_bytes().send_form().send_json() 发送的请求中,库会发送 Content-Length 头部。如果你发送一个正文大小未知的请求,使用 .send(),它接受一个 Read,ureq 将发送 Transfer-Encoding: chunked,并相应地编码正文。无正文请求(GET 和 HEAD)使用 .call() 发送,ureq 不会添加 Content-Length 或 Transfer-Encoding 头部。

如果你在发送正文之前设置了 Content-Length 或 Transfer-Encoding 头部,ureq 会尊重该头部,不会覆盖它,并按照你设置的头部指示编码正文或不对正文进行编码。

let resp = ureq::post("http://my-server.com/ingest")
    .set("Transfer-Encoding", "chunked")
    .send_string("Hello world");

字符编码

通过启用 ureq = { version = "*", features = ["charset"] } 功能,库支持发送/接收除 utf-8 之外的其他字符集。

对于 response.into_string(),我们读取头部 Content-Type: text/plain; charset=iso-8859-1,如果其中包含字符集指定,我们尝试使用该编码解码正文。如果没有,或者无法解释字符集,我们将回退到 utf-8

类似地,当使用 request.send_string() 时,我们首先检查用户是否设置了 ; charset=<whatwg charset> 并尝试使用该编码对请求数据进行编码。

代理

ureq 支持两种代理类型,HTTP (CONNECT),SOCKS4SOCKS5,前者始终可用,而后者必须通过启用功能 ureq = { version = "*", features = ["socks-proxy"] } 来启用。

代理设置在 Agent 上配置(使用 [AgentBuilder])。所有通过该代理发送的请求都将进行代理。

使用 HTTP 的示例

fn proxy_example_1() -> std::result::Result<(), ureq::Error> {
    // Configure an http connect proxy. Notice we could have used
    // the http:// prefix here (it's optional).
    let proxy = ureq::Proxy::new("user:[email protected]:9090")?;
    let agent = ureq::AgentBuilder::new()
        .proxy(proxy)
        .build();

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

使用 SOCKS5 的示例

fn proxy_example_2() -> std::result::Result<(), ureq::Error> {
    // Configure a SOCKS proxy.
    let proxy = ureq::Proxy::new("socks5://user:[email protected]: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 配置的服务器进行交互,则可能需要使用 native-tls。

以下是一个使用 native-tls 构建代理的示例。它需要启用 "native-tls" 功能。

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

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

受信任的根

当您使用 rustls(tls 功能)时,ureq 默认信任 webpki-roots,它是包含在您的程序中的 Mozilla 根计划的副本(因此,如果您的程序未更新,则不会更新)。您可以选择配置 rustls-native-certs,它从您的操作系统的信任存储中提取根。这意味着它将在您的操作系统更新时更新,并且它还将包括本地安装的根。

当您使用 native-tls 时,ureq 将使用您的操作系统证书验证器和根存储。

为了简单起见,使用阻塞 I/O

Ureq 使用阻塞 I/O 而不是 Rust 的新版 异步 (async) I/O。异步 I/O 允许在不增加内存和操作系统线程的高成本的情况下,服务多个并发请求。但是,这带来了复杂性。异步程序需要拉入运行时(通常是 async-stdtokio)。它们还需要任何可能阻塞的方法的异步版本,以及任何可能调用可能阻塞的另一个方法的任何方法的异步版本。这意味着异步程序通常有很多依赖项 - 这增加了编译时间,并增加了风险。

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

这就是为什么 ureq 使用阻塞 I/O 并计划保持这种方式。其他 HTTP 客户端提供异步 API 和阻塞 API,但我们想提供不带异步 API 所需所有依赖项的阻塞 API。


Ureq 受到其他优秀的 HTTP 客户端如 superagentfetch API 的启发。

如果您正在寻找的不是 ureq,请查看这些其他 Rust HTTP 客户端:surfreqwestisahcattohttpcactix-webhyper

依赖项

~1–14MB
~231K SLoC