LoginSignup
14
9

More than 1 year has passed since last update.

[Rust] hyperのClient機能のまとめ

Last updated at Posted at 2021-06-30

[Rust] hyperのClient機能のまとめ

RustのHttpクライアントとして、hyperreqwestがスター数も多く有名です。

この記事ではhyperについて紹介していきます。

ちなみにhyperはサーバーサイドの機能を併せ持つ低レベルに動くライブラリですが、クライアント機能に絞って紹介をさせてください。

hyperとは

hyperは低レベルでhttpリクエストを行うことができるクレート(ライブラリ)です。

低レベルということで高機能に柔軟に動かすことができますが、用件がシンプルなものであれば、hyperを内部で呼び出しているreqwestなども検討してみても良いと思います。

非同期型で動作し、クライアント、サーバー両方のAPIを提供していて、本番環境においても広く使われています。

使用方法

まず、Cargo.tomlに依存関係を追記します。

[dependencies]
hyper = { version = "0.14", features = ["full"] }
tokio = { version = "1", features = ["full"] }

Getでのリクエスト

tokioのmain関数内にシンプルなリクエスト構文を記述しています。

use hyper::Client;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // This is where we will setup our HTTP client requests.
    // Still inside `async fn main`...
    let client = Client::new();

    // Parse an `http::Uri`...
    let uri = "http://httpbin.org/ip".parse()?;

    // Await the response...
    let resp = client.get(uri).await?;

    println!("Response: {}", resp.status());

    Ok(())
}

実行結果

% cargo run                                      
    Blocking waiting for file lock on build directory
    Finished dev [unoptimized + debuginfo] target(s) in 4.70s
     Running `target/debug/demo`
Response: 200 OK

次に、レスポンスのbodyが標準出力されるように変更を加えます。

変更後のコード

use hyper::Client;
use hyper::body::HttpBody as _;
use tokio::io::{stdout, AsyncWriteExt as _};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {

    let client = Client::new();
    let uri = "http://httpbin.org/ip".parse()?;
    let mut resp = client.get(uri).await?;

    while let Some(chunk) = resp.body_mut().data().await {
        stdout().write_all(&chunk?).await?;
    }

    Ok(())
}

実行結果

% cargo run                                     
   Compiling demo v0.1.0 (/Users/ishikawashou/Desktop/work/note/RustNote/demo)
    Finished dev [unoptimized + debuginfo] target(s) in 4.72s
     Running `target/debug/demo`
{
  "origin": "60.76.119.106"
}

Postでのリクエスト

クライアントに渡すリクエストを生成し、Postリクエストを行います。

Request::builder()を使うことで非常に簡単にヘッダー付きのリクエストを生成することができます。

use hyper::{Client,Body, Method, Request};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let req = Request::builder()
        .method(Method::POST)
        .uri("http://httpbin.org/post")
        .header("content-type", "application/json")
        .body(Body::from(r#"{"library":"hyper"}"#))?;

    let client = Client::new();

    let resp = client.request(req).await?;

    println!("Response: {}", resp.status());
    Ok(())
}

実行結果

% cargo run                                   
   Compiling demo v0.1.0 (/Users/ishikawashou/Desktop/work/note/RustNote/demo)
    Finished dev [unoptimized + debuginfo] target(s) in 3.62s
     Running `target/debug/demo`
Response: 200 OK

並列リクエスト

今まではawaitを用いてかなり同期的にリクエストを行なっていましたが、複数のリクエストを並行して行うこともできます。

まず、futuresを使うのでCargo.tomlを更新します。

[dependencies]
hyper = { version = "0.14", features = ["full"] }
tokio = { version = "1", features = ["full"] }
futures = "0.3"

asyncを使い非同期ブロックを生成し、以下のように並列にリクエスト処理を行うことができます。

use hyper::{Client, Uri};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let client = Client::new();

    let ip_fut = async {
        let resp = client.get(Uri::from_static("http://httpbin.org/ip")).await?;
        hyper::body::to_bytes(resp.into_body()).await
    };
    let headers_fut = async {
        let resp = client.get(Uri::from_static("http://httpbin.org/headers")).await?;
        hyper::body::to_bytes(resp.into_body()).await
    };

    // Wait on both them at the same time:
    let (ip, headers) = futures::try_join!(ip_fut, headers_fut)?;

    let ip_body = String::from_utf8(ip.to_vec()).expect("response was not valid utf-8");
    println!("ip_body: {}", ip_body);

    let headers_body = String::from_utf8(headers.to_vec()).expect("response was not valid utf-8");
    println!("headers_body: {}", headers_body);

    Ok(())
}

実行結果

% cargo run
   Compiling demo v0.1.0 (/Users/ishikawashou/Desktop/work/note/RustNote/demo)
    Finished dev [unoptimized + debuginfo] target(s) in 4.37s
     Running `target/debug/demo`
ip_body: {
  "origin": "60.76.119.106"
}

headers_body: {
  "headers": {
    "Host": "httpbin.org", 
    "X-Amzn-Trace-Id": "Root=1-60dc0ede-62d5d0fa3966f7a94c0d0f02"
  }
}

TLSを使ったHTTPSへの接続

hyperのデフォルトのクライアントでは、TLSの実装が搭載されていないため、HTTPアドレスにしかアクセスできません。

Client::new()HttpConnectorを返すことからもそのような実装がされていることがわかります。

hyperではプラグインとしてHttpsConnectorを備えたhyper-tls crateを提供していますのでそれを使いましょう。

use hyper_tls::HttpsConnector;
use hyper::Client;

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>>{
    let https = HttpsConnector::new();
    let client = Client::builder().build::<_, hyper::Body>(https);

    let res = client.get("https://hyper.rs".parse()?).await?;
    assert_eq!(res.status(), 200);
    Ok(())
}

以上になります。何か質問、間違い等ございましたら@ishishowまでご連絡ください。
また、この記事はhyperのgetting startedを参考にしています。

参考文献

14
9
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
9