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


h2oとmruby

ある日、

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

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

mrubyでどのように返すデータを持つか

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

h2oの用意

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

Dockerfile
FROM unasuke/h2o-alpine:v2.0.4

COPY . /etc/h2o
CMD ["h2o", "--mode=master", "-c", "h2o.conf"]
h2o.conf
listen: 80
hosts:
  "127.0.0.1:80":
    paths:
      "/":
        mruby.handler-file: server.rb
access-log: /dev/stdout
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

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

すると、このように8080番ポートでアクセスできるようになりました。mrubyがresponseを返しているのがわかります。

mrubyがyamlを読みこむために

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

そこで、そのようなh2oを用意しました。
unasuke/h2o at mruby-yaml

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

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

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

yamlを読む

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

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

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

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

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

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


余談

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