mrubyでyamlを読みこんでh2oが返す

## h2oとmruby
ある日、

__「h2oとmruby使ったらhtmlがなくてもresponse返せるんじゃないか?」__

と思ったので、やってみることにしました。

## mrubyでどのように返すデータを持つか
さて、responseの内容をmruby scriptの中にベタ書きするのはカッコ悪そうなので、yamlに記述して、外部から読みこむことにします。

## h2oの用意
ひとまず、mrubyで簡単なresponseを返すh2oを用意します。

```dockerfile:Dockerfile
FROM unasuke/h2o-alpine:v2.0.4

COPY . /etc/h2o
CMD ["h2o", "--mode=master", "-c", "h2o.conf"]
```

```yaml:h2o.conf
listen: 80
hosts:
  "127.0.0.1:80":
    paths:
      "/":
        mruby.handler-file: server.rb
access-log: /dev/stdout
```

```ruby:server.rb
class Server
  def call(env)
    [200,
     {
       "content-type" => "text/plain; charset=utf-8",
     },
     ["Hello mruby in h2o!"]
    ]
  end
end

Server.new
```

[Create simple server · unasuke/mruby_ac2016@27cf7eb](https://github.com/unasuke/mruby_ac2016/commit/27cf7ebe265141a0c5180a7b61497b521cdb7e5b)

```s
# buildしてrun
% docker build -t h2o_mruby .
% docker run -it -p 8080:80 h2o_mruby
```

すると、このように8080番ポートでアクセスできるようになりました。mrubyがresponseを返しているのがわかります。
![response from server.rb](https://qiita-image-store.s3.amazonaws.com/0/18796/61973ce8-de74-6b4e-bfed-1ec9cc37333b.png)

## mrubyがyamlを読みこむために
mruby scriptの中でyamlを読みこむためには、[AndrewBelt/mruby-yaml](https://github.com/AndrewBelt/mruby-yaml)を使う必要があります。が、このmrbgemは素のh2oには含まれていないので、mruby-yamlを含んだh2oをbuildしなくてはいけません。

そこで、そのようなh2oを用意しました。
[unasuke/h2o at mruby-yaml](https://github.com/unasuke/h2o/tree/mruby-yaml)

用意といっても簡単で、`deps` 以下にmruby-yamlをcloneしてくるだけです。

そして、Dockerfileを次のように変更して、h2oをbuildします。

```dockerfile:Dockerfile
FROM alpine

RUN apk add --no-cache --virtual=builddeps \
        bison           \
        ca-certificates \
        cmake           \
        curl            \
        gcc             \
        git             \
        g++             \
        libc-dev        \
        linux-headers   \
        make            \
        openssl         \
        perl            \
        ruby            \
        ruby-dev        \
        wget            \
        yaml-dev        \
        zlib-dev        \
    && git clone https://github.com/unasuke/h2o.git \
    && cd h2o \
    && git checkout mruby-yaml \
    && cmake -DWITH_BUNDLED_SSL=on -DWITH_MRUBY=on . \
    && make install \
    && cd .. \
    && rm -rf h2o-* \
    && apk del builddeps \
    && apk add --no-cache --virtual=rundeps \
        libstdc++ \
        perl

RUN mkdir /etc/h2o

WORKDIR /etc/h2o

EXPOSE 80 443

COPY . /etc/h2o
CMD ["h2o", "--mode=master", "-c", "h2o.conf"]
```

[Build h2o with mruby-yaml · unasuke/mruby_ac2016@4a3c552](https://github.com/unasuke/mruby_ac2016/commit/4a3c5529b107cfb3474cb57a90ff7badc1546736)

## yamlを読む
いろいろ準備が整ったので、yamlを読みこんでresponseを返していきます。

このようなyamlを用意しました。

```yaml:data.yml
responses:
  -
    path: "/"
    message: "index"
  - 
    path: "/hoge"
    message: "hoge"
  - 
    path: "/fuga"
    message: "fuga"
```

そして、その内容に従ってresponseを返すように、mruby scriptを次のように書きかえます。

```ruby:server.rb
class Server
  def call(env)
    response = fetch_response env['PATH_INFO']
    if response
      [200,
       {
         "content-type" => "text/plain; charset=utf-8",
       },
       [response]
      ]
    else
      [404,
       {
         "content-type" => "text/plain; charset=utf-8",
       },
       ["Not found"]
      ]
    end
  end

  def fetch_response(path)
    yaml = YAML.load(File.open('data.yml').read)
    message = nil
    yaml['responses'].each do |res|
      if res['path'] == path
        message = res['message']
      end
    end
    message
  end
end

Server.new
```

[Return response from yaml · unasuke/mruby_ac2016@125a17b](https://github.com/unasuke/mruby_ac2016/commit/125a17b0e3291451fee4efb126e2412d71c611ee)

docker buildしてrunすると、このようにyamlの内容に従ってresponseが返ってくるようになりました。

![localhost:8080/](https://qiita-image-store.s3.amazonaws.com/0/18796/50f76fa1-3412-08c1-25d0-5b3dc60fe58a.png)
![localhost:8080/hoge](https://qiita-image-store.s3.amazonaws.com/0/18796/62eba900-cb01-cced-df1e-76a4de05634a.png)

## 余談
本当はhaconiwaのなかでh2oを動作させたかったのですが、どうにもうまくできず諦めました。