LoginSignup
5
3

More than 5 years have passed since last update.

mrubyのHTTP2サーバーのTrusterdでRackベースのフレームワークを動かす

Last updated at Posted at 2015-12-23

はじめに

Trusterdはこのアドベントカレンダーの主催者の@matsumotoryさんが開発されているmrubyで書かれたHTTP2サーバーです。

h2oやngx_mruby、mod_mrubyでRack APIベースのAPIに移行(もしくは対応)し、
これを機にmruby-hibariというフレームワークが開発されました。

今回はTrusterdでこのmruby-hibariを動かそうという記事になります。

Rack APIとは

によると、

Rubyにおけるサーバとアプリケーション/フレームワーク間のインターフェースの役割を果たすライブラリ

とのことでした。Perlで言う所の、PSGIということのようでした。

そんなこと言われても何となくわかるだけで、そんな状態では、trusterdをRack APIに
対応するなんてできません。

コードで考えるRack API

幸い、h2oがRack APIに移行した頃、Githubのリポジトリの追っかけやっておいたので、
h2oのテストコードもRack対応の為に変更されており、以下の

Proc.new do |env|
  [200, {"content-type" => "text/plain; charset=utf-8"}, ["hello from h2o_mruby\n"]]
end

というコードを知ることができ、これがRackを理解する大きな手がかりになりました。

サーバー側からは渡されたブロックに対して、Rackが規定する項目をハッシュに詰め込んで
呼べば良いようです。
(さらっと書いていますが、一週間くらい自分は考えようやく理解しました。)

その後、Qiitaに

という素晴らしい記事があり、灯台もと暗しといった感じでした。

この記事のおかげで、サーバ側にどんなものを返せば良いのかがすぐ分かりました。

trusterdとRack APIの対応

規格書を地道に実装するような柄ではないので、動かしてみたい、mruby-hibariに
ターゲットを定め、作業を続けます。

現状のHibariでは

を見れば分かるように
trusterdからブロックに渡す際の引数envに
以下の属性が設定されていないと、何かマズそう気配。

これらの属性のtrusterdでの対応するものを書き出すと

Rack trusterd
REQUEST_METHOD s.method
SCRIPT_NAME s.filename
PATH_INFO s.uri
REQUEST_URI s.unparsed_uri
QUERY_STRING s.args および、 POST時のパラメータも設定
SERVER_NAME s.hostname
SERVER_ADDR
SERVER_PORT
REMOTE_ADDR s.client_ip
REMOTE_PORT

サーバー側のIPアドレスとポート番号も設定ファイルに記述してるから、
参照できるが、Trusterd側のC言語でこれらの属性へのgetterを生やす必要がある。

暫定対応

とにかくhibariが動くか試したいので、/rack/hoge
でカレントディレクトリのhoge.rbを読み込みこれをRack APIで記述された
コードとして実行するような設定ファイルを書きました。

SERVER_NAME = 'Trusterd'
SERVER_VERSION = '0.0.1'
SERVER_DESCRIPTION = "#{SERVER_NAME}/#{SERVER_VERSION}"

root_dir = '.'

def LibTrasterdRackApi(env, filename, s)
  f = File.open(filename, 'r')
  script = f.read
  f.close
  # puts script
  # envをRack APIもどきに設定
  env['REQUEST_METHOD'] = s.method
  env['SCRIPT_NAME'] = s.filename
  env['PATH_INFO'] = s.uri
  env['REQUEST_URI'] = s.unparsed_uri
  env['QUERY_STRING'] = s.args
  env['QUERY_STRING'].slice!(0)
  env['SERVER_NAME'] = s.hostname
  env['SERVER_ADDR'] = '127.0.0.1' # TODO: s.server_host
  env['SERVER_PORT'] = '-1' # TODO s.port
  env['REMOTE_ADDR'] = s.client_ip
  env['REMOTE_PORT'] = '-1' # TODO

  p env['QUERY_STRING']

  # POSTの場合、QUERY_STRINGに格納する
  if (s.method == 'POST')
    if s.args.length < 1
      env['QUERY_STRING'] = s.body
    else
      env['QUERY_STRING'] = env['QUERY_STRING'] + '&' + s.body
    end
  end

  # p env
  ro = eval(script)
  # e={}
  ro.call(env)
end

s = HTTP2::Server.new(
  port: 8080,
  document_root: "#{root_dir}/htdocs",
  server_name: SERVER_DESCRIPTION,
  worker: 'auto',

  # required when tls option is true.
  # tls option is true by default.
  key: "#{root_dir}/ssl/key.pem",
  crt: "#{root_dir}/ssl/cert.pem",

  callback: true,

# connection_record defualt: true
# :connection_record => false,
)

s.set_map_to_storage_cb do
  if s.request.uri =~ /rack\/(.*)/
    s.set_content_cb do
      # puts "/rack/ start" + $1 + ".rb"
      response = LibTrasterdRackApi(s.request_headers.all, Regexp.last_match(1) + '.rb', s)
      # p response
      # s.set_status response[0]
      response[2].each { |line| s.rputs line }
      # s.rputs "rack end"
    end
end

s.run

hibariを使ったサンプル

一応、POSTメッソッドも適当に実装したで、GETとPOSTできるように
以下のサンプルでテスト。

class MyApp < Hibari::App
  def build
    res.code = 200
    res.headers["content-type"] = "text/html; charset=utf8"
    res.body.push("<html><body>")
    res.body.push("<h1>Hello, trusterd!</h1>")
    res.body.push("<hr><form action=\"/rack/hibari?getparam=1\" method=\"post\">")
    res.body.push("<input type=\"text\" name=\"postparam\"><input type=\"submit\"></form>")
    req.params.each do |k,v|
      res.body.push("#{k}: #{v}<br>")
    end
    res.body.push("</body></html>")
  end
end

MyApp.new.run

動いた

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3