M2 MacでHTTP3に対応したcurlをビルドする

## 概要
自前でHTTP3の検証環境を構築するために、Apple Siliconを搭載したMacでHTTP3に対応したcurlをビルドしたので、その過程などを備忘録も兼ねて残しておきます。

自分も、ネットワークなどについて特段詳しいというわけではないので、初心者と同じようにあれこれ試しながら進めた過程です。 **分かりにくいところや誤った内容は、コメントなどで教えていただければ幸いです。**

### 環境
- チップ： Apple M2
- OS： macOS 13.5
- コンパイラ： Apple clang version 15.0.0 (clang-1500.0.40.1)

## 周辺情報の整理
まず大前提として、**デフォルトのcurlではHTTP3には対応していない**ようです。そのため、HTTP3に対応させるためには、自分で設定をした上でcurlをビルドする必要があります。

親切なことに、curlのリポジトリ内にはHTTP3に対応したcurlをビルドするためのドキュメントが用意されています。なので、基本はここに沿って進めていきまが、ところどころ詰まった箇所もあるので、そこを補いながら書いていければなと思っています。

https://github.com/curl/curl/blob/master/docs/HTTP3.md

### 周辺ライブラリについて
HTTP3及びその下層のQUICのプロトコルに対応した実装は、外部ライブラリを組み込んで機能させるようです。

本記事ではQUICの実装として[**ngtcp2**](https://github.com/ngtcp2/ngtcp2)を、HTTP3の実装として[**nghttp3**](https://github.com/ngtcp2/nghttp3)を使用します。このうちngtcp2のビルド時にはngtcp2のリポジトリのREADMEも参照したので、そのリンクも貼っておきます。

https://github.com/ngtcp2/ngtcp2

curlのリポジトリでは他にもQUICの実装としてquicheとmsh3が紹介されていましたが、どちらも実験段階ということなので、今回は無難にngtcp2を用いたものにしました。

併せて、QUICに対応したTLSライブラリが必要でしたので、今回はcurlのリポジトリのドキュメントでも一番上に記載のある[**quictl**](https://github.com/quictls/openssl)を利用することにしました。OpenSSLというライブラリがデフォルトではQUICに対応しておらず、そこからフォークしてQUICに対応させた実装のようです。curlのリポジトリにはGnuTLSあるいはwolfSSLを用いたビルド方法も記載があります。

## ビルドの実施
### 依存ライブラリのダウンロード
はじめに[ngtcp2](https://github.com/ngtcp2/ngtcp2)のREADMEに[ビルドのために必要なライブラリ](https://github.com/ngtcp2/ngtcp2#requirements)が紹介されていたので、それらをダウンロードしました。記載のあったのは以下のライブラリです。

- pkg-config >= 0.20
- autoconf
- automake
- autotools-dev
- libtool

このうちautotools-devはインストール方法がわからず飛ばしたのですが、ビルドはできました。それ以外のライブラリは、以下のコマンドでインストールをしました。

```bash
brew install pkg-config
brew install autoconf
brew install automake
brew install libtool
```

### quictlsのビルド
[curlの手順書](https://github.com/curl/curl/blob/master/docs/HTTP3.md#build-with-quictls)どおりにビルドしました。

ここに記載の`<somewhere1>`という部分が最初なんのことか分からなかったのですが、ビルドのパスを指定して、かつ後々の手順の中で同じパスを指定してねという、変数みたいな役目で使っているようです。この先同様に`<somewhere1>`が出てきたら、それはこのquictlsのビルドフォルダを指します。

```bash
git clone --depth 1 -b openssl-3.1.4+quic https://github.com/quictls/openssl
cd openssl
# ./config enable-tls1_3 --prefix=<somewhere1>
./config enable-tls1_3 --prefix=$PWD/build
make
make install
```

### nghttp3のビルド
ここも[curlの手順書](https://github.com/curl/curl/blob/master/docs/HTTP3.md#build-with-quictls)どおりにビルドしていきます。

この`make`や`make install`実行時になんだかログを吐いて最初エラーかと思いましたが、明確にErrorという表記がなければ問題なさそうです。エラーの時はいかにもなエラーが大量に出ます😨

```bash
cd ..
git clone -b v1.0.0 https://github.com/ngtcp2/nghttp3
cd nghttp3
autoreconf -fi
# ./configure --prefix=<somewhere2> --enable-lib-only
./configure --prefix=$PWD/build --enable-lib-only
make
make install
```

### ngtcp2のビルド
ここだけ[curlの手順書](https://github.com/curl/curl/blob/master/docs/HTTP3.md#build-with-quictls)と少し異なった手順でビルドをしました。正確にいうと、[ngtcp2のREADME](https://github.com/ngtcp2/ngtcp2#build-from-git)にある手順に従いました。

該当箇所のコードは以下です。色々とコメントでも書いてあります。まず`PKG_CONFIG_PATH`のオプションについては下記の通り、今までビルドしてきたquictlsとnghttp3のビルドフォルダのパスを指定します。

それから、[ngtcp2のREADME](https://github.com/ngtcp2/ngtcp2#build-from-git)の実行コマンドには、Macのユーザならいくつかのオプションを追加してね的なことが書いてあるので、それを追加します。

具体的には、LDFLAGSオプションの末尾に`,-L/opt/homebrew/lib`を追記し、さらに新しく`CPPFLAGS="-I/opt/homebrew/include"`というオプションを追加しました。

[ngtcp2のREADME](https://github.com/ngtcp2/ngtcp2#build-from-git)のコマンドにはパスとして`/opt/local/lib`などが記載されていましたが、AppleSiliconのコンピュータだと`/opt/homebrew/lib`のようになるかなと思います。前者で記載して実行した場合、そのフォルダは空ですとwarningが出ました。

```bash
cd ..
git clone -b v1.0.1 https://github.com/ngtcp2/ngtcp2
cd ngtcp2
autoreconf -fi

# curlのREADMEに記載されているコマンドはこっち
# ./configure PKG_CONFIG_PATH=<somewhere1>/lib/pkgconfig:<somewhere2>/lib/pkgconfig LDFLAGS="-Wl,-rpath,<somewhere1>/lib" --prefix=<somewhere3> --enable-lib-only

# 公式のリポジトリにあるコンフィグコマンドはこっち
# For Mac users who have installed libev with MacPorts, append
# ',-L/opt/local/lib' to LDFLAGS, and also pass
# CPPFLAGS="-I/opt/local/include" to ./configure.
# For OpenSSL >= v3.0.0, replace "openssl/build/lib" with
# "openssl/build/lib64".
# 元のコマンド
# ./configure PKG_CONFIG_PATH=$PWD/../openssl/build/lib/pkgconfig:$PWD/../nghttp3/build/lib/pkgconfig LDFLAGS="-Wl,-rpath,$PWD/../openssl/build/lib"
# macなので指示通りパスを追加してみる
# Apple Siliconのmacだと/opt/local/*ではなく/opt/homebrew/*かも
./configure PKG_CONFIG_PATH=$PWD/../openssl/build/lib/pkgconfig:$PWD/../nghttp3/build/lib/pkgconfig LDFLAGS="-Wl,-rpath,$PWD/../openssl/build/lib,-L/opt/homebrew/lib" CPPFLAGS="-I/opt/homebrew/include" --prefix=$PWD/build --enable-lib-only

make
make install
```

### curlのビルド
最後にcurl本体のビルドです。これも基本は[curlの手順書](https://github.com/curl/curl/blob/master/docs/HTTP3.md#build-with-quictls)通りに実行しました。

最後のmake installだけ、そのまま実行するとエラーになってしまいました。エラー内容がインストールする権限が無いよ的なものだったため、sudoをつけて実行すると最後まで動作しました。これもAppleSilicon故の環境の違いが原因なのかなと考えています。

sudoでの実行に抵抗のある方は、実行しないままでも/src配下に実行ファイルがあるかと思うので、それを利用する形で問題ないかと思います。

```bash
cd ..
git clone https://github.com/curl/curl
cd curl
autoreconf -fi
# LDFLAGS="-Wl,-rpath,<somewhere1>/lib" ./configure --with-openssl=<somewhere1> --with-nghttp3=<somewhere2> --with-ngtcp2=<somewhere3>
LDFLAGS="-Wl,-rpath,$PWD/../openssl/build/lib" ./configure --with-openssl=$PWD/../openssl/build --with-nghttp3=$PWD/../nghttp3/build --with-ngtcp2=$PWD/../ngtcp2/build
make
# make install
sudo make install
```

これにてcurlをビルドすることができました。実行ファイルは`/usr/local/bin/curl`もしくは、上の操作時点からフォルダを移動していなければ`./src/curl`にもありました。

## curlの動作確認
curlの動作を確認します。HTTP3に対応しているいずれかのサイト（記事の例ではwww.google.com）に`--http3-only`と`-v`のオプションをつけてcurlを実行してみます。レスポンスのボディは必要ないので、捨てています。

```bash
/usr/local/bin/curl --http3-only https://www.google.com -v 1> /dev/null
```

結果として、以下のような記述が見られれば成功でしょう。

```bash
* using HTTP/3
* [HTTP/3] [0] OPENED stream for https://www.google.com/
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351104/62c54b15-a858-8ddf-ce67-3ee176322a0f.png)

qlogファイルの出力も以下のようなコマンドでできました。

```bash
QLOGDIR=./ /usr/local/bin/curl --http3-only https://www.google.com -v 1> /dev/null
```

## 感想
完全に記事の本旨とは異なりますが、備忘録も兼ねているのでここまで作業しての私個人の感想的なものです。

自分の知識不足でもありますが、curlの中で使うQUICやHTTP3、TLSのライブラリを別でビルドして組み込むっていうことをしているんだなーと単純に感心しながらやっていました。てっきりそういう部分もcurl内で既にプログラム書かれているものかと思っていました。外部ライブラリを使うことで、実装をシンプルにかつ高機能にできるといった具合なのでしょうか。

それから、TLSのライブラリ群について、OpesSSLはこの分野あまり詳しくない自分でも名前を見聞きしたことがあるくらい主流なライブラリだと思っていたので、まさかまだQUICに対応していないとは…という気分でした。規模が大きければ大きいほど、動きづらくなってしまうということなんでしょうか。

今回はcurlのビルドでしたが、併せてはHTTP3に対応したnginxのビルドにも取り組んでいるので、その件もまたQiitaにまとめられればと思います。

## 参考文献・関連リンク
https://gihyo.jp/admin/serial/01/http3/0004

https://http3-explained.haxx.se/

https://qiita.com/MHKS/items/3a57f67f00a2055429a0

https://www.weblio.jp/content/GNU+Autoconf

https://qiita.com/impepc/items/1bfdfe8ad741def10948

https://zenn.dev/nkzn/articles/brew3-should-install-with-m1-terminal

https://atmarkit.itmedia.co.jp/bbs/phpBB/viewtopic.php?topic=3865&forum=10

https://github.com/quictls/openssl

https://github.com/ngtcp2/nghttp3

https://github.com/ngtcp2/ngtcp2

https://github.com/curl/curl/blob/master/docs/HTTP3.md#build-with-quictls