This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

More than 5 years have passed since last update.

Rustで非同期処理入門ついでにリクエスト入門 メモ

Last updated at Posted at 2018-03-07

はじめに

個人的なメモなので、間違っている可能性が高い記事です。
質問やマサカリがあれば、コメントか@miyagaw61までお願いします。

futures

Future(計算結果が非同期で返るResultのようなもの)を提供するためのクレートです。

tokio_core

非同期ネットワーククレートです。

hyper

HTTPリクエストをするためのクレートです。

hyper_tls

HTTPSリクエストをするためのクレートです。

GETリクエスト(HTTP)実装

宣言

extern crate futures;
extern crate tokio_core;
extern crate hyper;

URL作成

let url = "http://hogehoge";
let url = url.parse::<hyper::Uri>().unwrap();

スキームチェック

if url.scheme() != Some("http") {
    println!("This example only works with 'http' URLs.");
    return;
}

非同期イベントループ作成

let mut core = tokio_core::reactor::Core::new().unwrap();

非同期イベントループのハンドラを作成

ハンドラとは、イベントループを参照する変数を表します。

let handle = core.handle();

クライアント作成

非同期処理では、このように引数にイベントループのハンドラを渡してFutureが実装された返り値を返すメソッドをもつインスタンスを作成する必要があります。

let client = Client::new(&handle);

イベントの定義

Futureが実装された型(以降Future)を用いてイベントループ上で実行させるイベントを定義します。
FutureFutureを返しそれがFutureを返す、というようにチェインさせていきます。
and_then()Futureを引数にとるクロージャを引数にとります。
and_then()は引数のクロージャに与えられたFutureをアンラップして、クロージャを実行させ、クロージャが返したResultFutureにラップ(変換かも?)して返します。
and_then()は引数がF ( F: FnOnce(Self::Item) -> B, B: IntoFuture<Item=(), Error=Self::Error> ) なので、and_then()に渡すクロージャは最後にItem=()ResultであるOk(())を返す必要があります(FnOnceはクロージャを表します)(Result型はIntoFuturetraitが実装されています)。

extern crate encoding_rs;
use encoding_rs::*;

let work = client.get(url).and_then(|res| {
    res.body().for_each(|chunk| {
        let (s, _, _) = UTF_8.decode(&chunk);
        println!("{}", s);
        Ok(())
    })
});

res.body()Bodyを返します。Body単体では複雑な構造体です。
Body.for_each()Bodyから1チャンクを取得し引数に与えたクロージャをループ処理するメソッドです。
for_each()ForEach<Self, F, U>U: IntoFuture<Item = (), Error = Self::Error>)を返すため、for_each()Item=()であるResultを返す必要があります。
チャンクがどういう区切りで分けられているかはわかりませんが、チャンクそのものはバイト列です。to_vec()メソッドでu8のベクタにすることができます。

また、Bodyの扱い方ですが、次のような書き方も可能です。

res.body().concat2().and_then(|body| {
    let (s, _, _) = UTF_8.decode(&body);
    println!("{}", s);
    Ok(())
})

この書き方では、concat2()メソッドによってチャンクを全て結合しています。
チャンクという馴染みのない概念を忘れることができるため、こちらの方が直感的かもしれません。

GETリクエスト(HTTPS)実装

宣言

extern crate hyper_tls;

クライアント生成

クライアント生成を以下の式に置き換えるだけで後は何も変える必要はありません。

extern crate hyper_tls;

let client = hyper::Client::configure()
    .connector(hyper_tls::HttpsConnector::new(4, &handle).unwrap())
    .build(&handle);

その他のリクエスト方法

Requestインスタンスを作成し、request()メソッドを使用する方法

let req = hyper::Request::new(hyper::Method::Get, url);
let work = client.request(req).and_then(|res| { /* ... */ } );

この方法であれば、GETリクエストだけでなくその他のメソッド(POSTなど)のリクエストも可能になります。
今後はこの方法が主流になりそう?

POSTリクエスト実装

自作ヘッダ付与(推奨)

Request.headers_mut().set_raw()メソッドを用います。

let mut req = hyper::Request::new(hyper::Method::Post, url);
req.headers_mut().set_raw("User-Agent", "archiveis (https://github.com/pastpages/archiveis)");
let work = client.request(req).and_then(|res| { /* ... */ });
core.run(work).unwrap();

自作ヘッダ付与(非推奨)

header!マクロを使用する方法です。
マクロを使用するために、最初のextern crate hyper#[macro_use]を付与する必要があります。
header!マクロでヘッダ付与関数を作成し、Request.headers_mut().set()に渡します。

#[macro_use]
extern crate hyper;

let mut req = hyper::Request::new(hyper::Method::Post, url);
header! { (MyUserAgent, "User-Agent") => [String] }
req.headers_mut().set(MyUserAgent("archiveis (https://github.com/pastpages/archiveis)".to_string()).to_owned());
let work = client.request(req).and_then(|res| { /* ... */ });
core.run(work).unwrap();

一般的なヘッダ

User-AgentHostなどの一般的なヘッダにおいては、ユーザ側でマクロを使わなくても既にヘッダ付与関数が用意されていることがあります。

extern crate hyper;

let mut req = hyper::Request::new(hyper::Method::Post, url);
req.headers_mut().set(hyper::header::UserAgent::new("archiveis (https://github.com/pastpages/archiveis)".to_string()).to_owned());
let work = client.request(req).and_then(|res| { /* ... */ });
core.run(work).unwrap();

ボディ付与

extern crate jsonway;

let json = jsonway::object(|json| {
    json.set("url", "https://www.kernel.org".to_string());
}).unwrap().to_string();
req.set_body(json);

エンコーディング

こちらを参照ください。

html解析

こちらを参照ください。

0

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