Rust

RustでHTTP2クライアントを試す

More than 1 year has passed since last update.

概要

RustでHTTP2クライアントを使いたい。
ざっくり調べた感じ solicithyperが使えそうなのでそれを試してみました。

前提

項目 説明
OS MacOSX 10.11.2
Rust 1.5.0 stable
検証日時 2016.01.15

solicit

http2-solicit.rs
extern crate solicit;
use solicit::http::client::CleartextConnector;
use solicit::client::SimpleClient;
use std::str;

static HOST: &'static str = "http2bin.org";

fn main() {
    let connector = CleartextConnector::new(HOST);
    let mut client = SimpleClient::with_connector(connector).unwrap();
    let resp = client.get(b"/get", &[]).unwrap();

    println!("{}", str::from_utf8(&resp.body).unwrap());
}

かなり小さいですが、HTTP2使ってアクセスできます。

{
  "args": {},
  "headers": {
    "Connection": "keep-alive",
    "Host": "http2bin.org",
    "Via": "2 http2bin.org"
  },
  "origin": "180.44.100.161",
  "url": "http://http2bin.org/get"
}

hyper

RustでHTTPを扱えるcrateと言えばhyperですが、hyperもHTTP2を扱うことができます。
中身はsolicitを使っているようでした。

http2-hyper.rs
extern crate hyper;
use std::io;
use hyper::Client;
use hyper::http::h2;

static URL: &'static str = "http://http2bin.org/get";

// http -> OK
// https -> Error:
//          thread '<main>' panicked at 'called `Result::unwrap()` on an `Err` value:
//          Io(Error { repr: Custom(Custom { kind: InvalidInput, error: StringError("Invalid scheme for
//          Http") }) })', ../src/libcore/result.rs:738
fn http2_with_error() {
    let client = Client::with_protocol(h2::new_protocol());
    let mut res = client.get(URL).send().unwrap();

    println!("Response: {}", res.status);
    println!("Headers:\n{}", res.headers);
    io::copy(&mut res, &mut io::stdout()).unwrap();
}

fn main() {
    http2_with_error();
}
Response: 200 OK
Headers:
content-type: application/json
access-control-allow-origin: *
server: h2o/1.6.1
content-length: 222
:status: 200
date: Thu, 14 Jan 2016 21:36:05 GMT
x-clacks-overhead: GNU Terry Pratchett
access-control-allow-credentials: true

{
  "args": {},
  "headers": {
    "Connection": "keep-alive",
    "Host": "http2bin.org,http2bin.org",
    "Via": "2 http2bin.org"
  },
  "origin": "180.44.100.161",
  "url": "http://http2bin.org,http2bin.org/get"
}

アクセスはできてそうですが、Hostとurlの値に少し違和感あります。どうも:authority:擬似ヘッダとHostヘッダを両方持っているようで、http2bin.orgで上記のように表示されるようです。

リクエスト時にHostヘッダを含めないパッチを作ってみましたが、かなり強引な対応なので今はそっと置いておこうかと思います。

httpsでアクセスしたい

上記のhyperの例でhttps://でアクセスしようとすると実行時に以下のようなエラーになります。

thread '<main>' panicked at 'called `Result::unwrap()` on an `Err` value: Io(Error { repr: Custom(Custom { kind: InvalidInput, error: StringError("Invalid scheme for Http") }) })', ../src/libcore/result.rs:738

https://でもアクセスできるような対応をいれたサンプルは以下です。

extern crate hyper;
use std::io::Read;
use hyper::Client;
use hyper::http::h2::Http2Protocol;
use hyper::net::{HttpsConnector, Openssl};

static URL: &'static str = "https://http2bin.org/get";
static CERT_FILE: &'static str = "tmp.crt";
static KEY_FILE: &'static str = "tmp.key";

fn http2() {
    let ssl = Openssl::with_cert_and_key(CERT_FILE, KEY_FILE).unwrap();
    let ssl_connector = HttpsConnector::new(ssl);
    let http2_client = Client::with_protocol(Http2Protocol::with_connector(ssl_connector));
    let mut res = http2_client.get(URL).send().unwrap();

    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();

    println!("{}", body);
}

fn main() {
    http2();
}

solicitでhttpsアクセス

featuresにtlsを含めろってことなので、それでビルドしてみてもビルドこけて使う事できませんでした。(nightlyとかだといけるのかもしれません。試してません。)

[dependencies.solicit]
version = "*"
features = ["tls"]