CowboyでHello Worldのサンプルを動かしてみる

最近、ElixirのPhoenixを使う機会が増えて来ました。そんなPhoenixですが内部的にErlangのHTTPサーバであるCowboyが使われています。内部的に使われてるので一度、Cowboyを使ってみたいと思い、Erlangで簡単なサンプルを実装してみましたのでその時のメモを書いていきます。

## さらっとCowboyの特徴

モダンなHTTPサーバになります。モダンと言ってるだけあってHTTP/2とWebSocketを対応しているようです。遅延とメモリ消費量が少なくコード量自体も少ないHTTPサーバになります。なので簡単なAPIサーバを実装するときにPhoenixでは重過ぎる場合とかに使えるかなぁと思います。

## 実行した環境

* Erlang/OTP 21
* rebar3 3.6.1
* Cowboy 2.4.0

## 実装
### 環境準備
#### Erlangとrebar3

まずは環境から。そもそものErlangの環境が必要になるので、Erlangが動作する + rebar3が動作する環境を準備します。自分はDockerで環境を準備しました。

```
docker pull erlang
```
これだけです。これでErlangの環境が手に入りました。便利ですねぇ
次は手に入れた環境を動かしてみます。

```
docker run -id -d -p 8010:8010 --name erlang erlang
```
これで、ローカルの8010ポートとDockerの8010ポートをマッピングしましたので、ローカルの8010にアクセスすると起動したErlangコンテナの8010にアクセスが行くようになります。なのでErlangコンテナ内ではCowboyを8010ポートで起動させるようにします。

公式のErlangイメージにはrebar3がすでに入っている状態です。

```
$ docker exec -it erlang rebar3 version
rebar 3.6.1 on Erlang/OTP 21 Erts 10.0.5
```

#### プロジェクトの作成

次は、rebar3でプロジェクトを作成します。
次のコマンドをコンテナ内で実行することでプロジェクトの雛形が作成されます。

```
rebar3 new app sandbox
```

今回は`sandbox`っと言う名前のプロジェクトを作成しました。
後日に詳しく書こうかと思いますが、rebar3でプロジェクトを作成する方法はいくつかあります。`rebar3 new app [アプリケーション名]`でプロジェクトを作成すると単独で動作するプロジェクトが作成されます。

### ディレクトリ構成

作成したばかりのプロジェクトのディレクトリ構造は以下のようになります。

```
sandobx
  |- .gitignore
  |- LICENSE
  |- README.md
  |- rebar.config
  |- src
      |- sandbox.app.src
      |- sandbox_app.erl
      |- sandbox_sup.erl
```

ここで、軽くそれぞれのファイルとディレクトリについて説明しておきます。

rebar.configはその名の通り、設定ファイルになります。ここに今回のプロジェクトに依存するライブラリを追加したり、コンパイル時のオプションを指定したりします。

sandbox.app.srcではプロジェクトの情報が記述されています。バージョンだったりとか依存関係のあるアプリケーションなどが記述されています。

sandbox_app.erlに実際にアプリケーションとして動かすコードを記述していきます。ここの`start`関数が一番最初に呼ばれます。

sandbox_sup.erlはプロジェクトのトップレベルのsupervisorになります。

### ハンドリングとコネクション

ではここから実際にアプリケーションを作成していきます。
sandbox_app.erlの`start`関数内に以下のコードを追加します。

```erlang:sandbox_app.erl
start(_StartType, _StartArgs) ->
    Dispatch = cowboy_router:compile([
        {'_', [
            {"/", toppage_handler, []}
        ]}
    ]),
    {ok, _} = cowboy:start_clear(http, [{port, 8010}], #{
        env => #{dispatch => Dispatch}
    }),
    sandbox_sup:start_link().
```

これはCowboyでのルーティングの設定になります。
`cowboy_router:compile`でルーティングの初期設定を行なっています。`compile`関数の引数でリストを渡した場合は、タプルの配列を渡します。上記のタプルでは` {HOST、PATH}` で渡しています。`'_'` は動作しているホスト自信を指定しています。`{"/", toppage_handler, []}`ではルートパスにアクセスが来たときにtoppage_handlerが実行されるようなパスの設定になります。

`cowboy:start_clear` 関数でTCPでの接続設定を行います。
`http` は単なる名前です。
`[{port, 8010}]` はリッスンするポートを指定します。
`#{env => #{dispatch => Dispatch}}`ではプロトコルの設定を行います。今回はHTTP/1.1を使うので`cowboy_http`の設定になります。dispatchで指定したものがルーティングで使われます。

### ハンドラーの設定

次にハンドラーの設定をしていきます。今回は、Helloをレスポンスした後に１秒スリープをし、次にWrokdをレスポンス。もう一度１秒スリープした後にCunked!をレスポンスするようにしました。

```erlang:toppage_handler.erl
-module(toppage_handler).

-export([init/2]).

init(Req0, Opts) ->
    Req = cowboy_req:stream_reply(200, Req0),
    cowboy_req:stream_body("Hello\r\n", nofin, Req),
    timer:sleep(1000),
    cowboy_req:stream_body("World\r\n", nofin, Req),
    timer:sleep(1000),
    cowboy_req:stream_body("Cunked!\r\n", fin, Req),
    {ok, Req, Opts}.
```

`cowboy_req:stream_reply`でレスポンスするヘッダーを作成します。
`cowboy_req:stream_body` レスポンスするbodyを作成します。第二引数の`nofin`では送信するデータが続くことを意味し、`fin`は送信データが終了したことを意味するようになります。

### 実行

最後にアプリケーションを実行させます。

```
rebar3 shell
```
shellで実行させ、`curl http://127.0.0.1:8010`を実行してみます。

```
curl http://127.0.0.1:8010
Hello
World
Cunked!
```

無事に、レスポンスが返って来ました！！

## まとめ

Cowboyとrebar3の情報が少なく苦戦しました。。。特にrebar3でのプロジェクトの作成、設定ファイルなどの情報がなく、ひたすらドキュメントをみてました。逆にドキュメントさえ読めば進められるので、ドキュメントに感謝です。Cowboyは割とサクサクとできましたし、サンプルも公開されているのでサンプルを見ながらすれば余裕です。

次は、もう少し、webっぽい動きのものを作っていきます。