[Rust] hyperのClient機能のまとめ
RustのHttpクライアントとして、hyperやreqwestがスター数も多く有名です。
この記事では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を参考にしています。
参考文献