HTTP/3 対応の curl を作る

# といっても

ほぼ、curl のページをなぞっただけですが……。
https://github.com/curl/curl/blob/master/docs/HTTP3.md

# ※ Docker 使えます

その前に。

Docker でお手軽に試せます。
https://hub.docker.com/r/keioni/curl-http3

```
docker run --rm keioni/curl-http3 curl -v -L -s --http3 https://quic.tech:8443
```

[HTTP/3 に対応させた nginx](https://qiita.com/keys/items/bf492ef543fde3d8d822) もあるので、併せて使えば HTTP/3 のテストが簡単にできます。

# build

ここでは、**nghttp3 + ngtcp2 版** と **quiche 版** の二つを紹介します。

現時点(2020年7月27日現在)では、どちらがいいとか悪いとかはない、と感じています[^draft]。

[^draft]: プロトコル自体が正式版になっていないので、この段階で速度がどうとか言うものでもないですし、コードは日々アップデートされていくので clone した版によっては不安定なこともあると思います

**[quiche](https://github.com/cloudflare/quiche)[^quiche_name]** は、CDNプロバイダの [Cloudflare](https://www.cloudflare.com) が作った実装で、エッジサーバで実際に使っているそうです。

[^quiche_name]: quiche は甘くないパイというかタルトのような「[キッシュ](https://www.google.com/search?q=%E3%82%AD%E3%83%83%E3%82%B7%E3%83%A5)」と同じ綴りです。ロゴもそれっぽいですね

**[nghttp3, ngtcp2](https://github.com/ngtcp2)** は、あまり詳しくないのですが、HTTP/2 の実装である nghttp2 に携わった方々によって作られている、もののようです。(違っていたらごめんなさい)

build するにあたっても、

* **quiche 版** は手順は少ないですが、BoringSSL や cargo のような、人によっては不慣れでトラブルに対応するのが難しいかもしれません
* **nghttp3 + ngtcp2 版** は OpenSSL ですし、おなじみの make を使うので単純ですが、手順が少し増えます

ぐらいの違いしかないので、好みで選んでいいのではないかなーと思います。

# nghttp3 + ngtcp2 版

## パッケージのインストール

まず、git と、gcc などおなじみの build 用パッケージも入れます。環境によってはすでに入ってたりすると思いますが、参考までに Amazon Linux 2 の Docker イメージで私が入れたパッケージをリストアップしました。

```
yum install git unzip gcc gcc-c++ make autoconf automake libtool \
pkgconfig
```

次に、build 対象が依存している開発用パッケージもを入れます。

```
yum install CUnit-devel libev-devel libnghttp2-devel
```

その他、build する curl で通信の圧縮ができるように zlib も入れておくといいかなーと。

```
yum install zlib-devel
```

## ビルド

URLは2020年7月20日時点の最新のものです。必要に応じて新しいものを確認してください。

インストール先は、ここでは `/opt/http3` にします。

```
extern PREFIX="/opt/http3"
```

### openssl (+ QUIC patch)

```
git clone --depth 1 -b OpenSSL_1_1_1g-quic-draft-29 https://github.com/tatsuhiro-t/openssl
cd openssl
./config --prefix=$PREFIX
make
make install_sw
```

### nghttp3

```
git clone https://github.com/ngtcp2/nghttp3
cd nghttp3
autoreconf -i -v
./configure --prefix=$PREFIX --enable-lib-only
make
make install
```

### ngtcp2

```
git clone https://github.com/ngtcp2/ngtcp2
cd ngtcp2
autoreconf -i -v
./configure --prefix=$PREFIX PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig
make
make install
```

### curl

```
git clone https://github.com/curl/curl
cd curl
./buildconf
./configure \
    LDFLAGS="-Wl,-rpath,$PREFIX/lib" \
    PKG_CONFIG_PATH="$PREFIX/lib/pkgconfig" \
    --prefix="$PREFIX" \
    --enable-alt-svc
make
make install
```

## 確認

できあがりました。中身を確かめてみます。

```
# /opt/http3/bin/curl -V
curl 7.72.0-DEV (x86_64-unknown-linux-gnu) libcurl/7.72.0-DEV OpenSSL/1.1.1g zlib/1.2.7 nghttp2/1.41.0 ngtcp2/0.1.0-DEV nghttp3/0.1.0-DEV
Release-Date: [unreleased]
Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS HTTP2 HTTP3 HTTPS-proxy IPv6 Largefile libz NTLM NTLM_WB SSL TLS-SRP UnixSockets
```

ちゃんと `nghttp3` と `ngtcp2` を使ってますね。

# quiche 版

## パッケージのインストール

まず、Rust が入っていない場合は [rustup](https://rustup.rs/) などを使って入れます。

次に、git と、gcc などおなじみの build 用パッケージも入れます。環境によってはすでに入ってたりすると思いますが、参考までに Amazon Linux 2 の Docker イメージで私が入れたパッケージをリストアップしました。

```
yum install git gcc gcc-c++ make automake autoconf libtool pkgconfig cmake3
```

通信の圧縮を有効にするときは `zlib-devel` を、HTTP/3 だけでなく HTTP/2 も有効にしたいときは `libnghttp2-devel` も導入します。

```
yum install zlib-devel
yum install libnghttp2-devel
```

## ビルド

cmake パッケージのバイナリが `cmake3` になっている場合は `cmake` にシンボリックリンクを張ります。

```
ln -s /usr/bin/cmake3 /usr/bin/cmake
```

### quiche + BoringSSL

BoringSSL を含んだ quiche を build します。

以下は、先に示したURLの中身をまるまるコピーしただけです。

```
git clone --recursive https://github.com/cloudflare/quiche
cd quiche
cargo build --release --features pkg-config-meta,qlog
mkdir deps/boringssl/src/lib
ln -vnf $(find target/release -name libcrypto.a -o -name libssl.a) deps/boringssl/src/lib/
```

### curl

curl 本体を build します。

```
cd ..
git clone https://github.com/curl/curl
cd curl
./buildconf
./configure \
    LDFLAGS="-Wl,-rpath,$PWD/../quiche/target/release" \
    --prefix="/opt/http3" \
    --with-ssl="$PWD/../quiche/deps/boringssl/src" \
    --with-quiche="$PWD/../quiche/target/release" \
    --enable-alt-svc
make
```

## 確認

できあがりました。中身を確かめてみます。

```
# /opt/http3/bin/curl -V
curl 7.72.0-DEV (x86_64-unknown-linux-gnu) libcurl/7.72.0-DEV BoringSSL zlib/1.2.7 nghttp2/1.41.0 quiche/0.5.1
Release-Date: [unreleased]
Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS HTTP2 HTTP3 HTTPS-proxy IPv6 Largefile libz NTLM NTLM_WB SSL UnixSockets
```

`quiche` が使われていることが分かります。

# 実行

実際に動作を試してみましょう。

```
/opt/http3/bin/curl -v --http3 https://quic.tech:8443
```

nghttp3 + ngtcp2 版と quiche 版では若干表示が違っています。

## nghttp3 + ngtcp2 版

こんな感じになるはずです。

```
*   Trying 45.77.96.66:8443...
* Connect socket 5 over QUIC to 45.77.96.66:8443
*   Trying 2001:19f0:5:c21:5400:1ff:fe33:3b96:8443...
* Immediate connect fail for 2001:19f0:5:c21:5400:1ff:fe33:3b96: Cannot assign requested address
* QUIC handshake is completed
* Connected to quic.tech () port 8443 (#0)
* Using HTTP/3 Stream ID: 0 (easy handle 0xaaaa00)
> GET / HTTP/3
> Host: quic.tech:8443
> user-agent: curl/7.72.0-DEV
> accept: */*
>
< HTTP/3 200
< server: quiche
< content-length: 462
< priority:
<!DOCTYPE html>

<html>
<head>
:
(省略)
:
</head>

<body>
    <div class="cont">
        <img src="/quic.svg" />
    </div>
</body>
</html>
* Connection #0 to host quic.tech left intact
```

## quiche 版

quiche 版だとこんな感じです。

```
*   Trying 45.77.96.66:8443...
* Sent QUIC client Initial, ALPN: h3-29,h3-28,h3-27
*   Trying 2001:19f0:5:c21:5400:1ff:fe33:3b96:8443...
* Immediate connect fail for 2001:19f0:5:c21:5400:1ff:fe33:3b96: Cannot assign requested address
* Connected to quic.tech (45.77.96.66) port 8443 (#0)
* h3 [:method: GET]
* h3 [:path: /]
* h3 [:scheme: https]
* h3 [:authority: quic.tech:8443]
* h3 [user-agent: curl/7.72.0-DEV]
* h3 [accept: */*]
* Using HTTP/3 Stream ID: 0 (easy handle 0x23969d0)
> GET / HTTP/3
> Host: quic.tech:8443
> user-agent: curl/7.72.0-DEV
> accept: */*
>
< HTTP/3 200
< server: quiche
< content-length: 462
< priority:
<
<!DOCTYPE html>

<html>
<head>
:
(省略)
:
</head>

<body>
    <div class="cont">
        <img src="/quic.svg" />
    </div>
</body>
</html>
* Connection #0 to host quic.tech left intact
```

リクエストの “`GET / HTTP/3`” と、レスポンスの “`HTTP/3 200`” が出ていれば成功です！　いえい！

# Alt-Svc

詳細は省きますが、[Alternate service (Alt-Svc)](https://http3-explained.haxx.se/ja/h3/h3-altsvc) にも対応しているので、その挙動を見るのにも使えます。

```
/opt/http3/bin/curl -v --alt-svc altsvc.cache https://quic.aiortc.org/
```

# さいごに

HTTP/3 対応のサイトは2020年7月27日現在、ほとんどありません。
今この curl を使うとしたら、自分で HTTP/3 サーバを建てるときでしょう。というわけで、[HTTP/3 対応の nginx を作る方法](https://qiita.com/keys/items/bf492ef543fde3d8d822)も書きました。よろしければぜひ。

あと、既存のテストサイトもあります。お手軽に試してみてくださいー！
https://bagder.github.io/HTTP3-test/

(あと、こっそり[私のサイト](https://devel.keys.jp)も対応させています。いつ止めるか分からないですが……)