Trusterd(mruby-http2)にリバースプロキシや動的コンテンツ機能を追加した

Trusterd(トラスタード)と呼んでいるmrubyで設定が書けるHTTP/2 Web Serverを、色々と勉強しながら実装しているのですが、それらに下記機能を試験的に追加しました。

  • HTTP/1へのリバースプロキシ機能
  • コンテンツとしてmrubyスクリプトを実行できる機能

これらの機能はまだ実装は甘く、性能・効率面はこれから詰めていきますが、一応動作するという事で簡単に紹介します。Trusterdの設定方法や簡単な紹介は、過去のエントリ「Trusterd: HTTP/2 Web Server scripting with mruby v0.0.1 リリースしました」を御覧ください

HTTP/1へのリバースプロキシ機能

これは、フロントをTrusterdを使ってHTTP/2で受けながら、バックエンドではHTTP/1なWebサーバにリバースプロキシして、そのデータをHTTP/2用に加工してHTTP/2クライアントにレスポンスを返す機能です。

設定は以下のように、set_map_to_strage_cbコールバックに書きます。

[program lang=’ruby’ escaped=’true’]

s.set_map_to_strage_cb {

   if s.request.uri =~ /^\/upstream(\/.*)/                                       

    s.upstream_uri = $1                                                         

    s.upstream = http://127.0.0.1                                             

  end

}

[/program]

/upstream/index.htmlというURIにアクセスが来たら、/upstream以下に続くURIである/index.htmlを127.0.0.1のサーバに対してリクエストを投げる、という内容になります。

これで実際に、フロントにTrusterdをHTTP/2で立ててバックエンドにApache httpdをHTTP/1で動かしておき、「upsream dayo」と書いただけのupstream.htmlをApache httpd上で配信できるようにしておいて、HTTP/2クライアントであるnghttpでTrusterdに対してリクエストを投げると、以下のようになります。

[program lang=’bash’ escaped=’true’]

$ nghttp -v http://127.0.0.1:8080/upstream/upstream.html

[  0.001] send SETTINGS frame <length=15, flags=0x00, stream_id=0>

          (niv=3)

          [SETTINGS_MAX_CONCURRENT_STREAMS(3):100]

          [SETTINGS_INITIAL_WINDOW_SIZE(4):65535]

          [SETTINGS_COMPRESS_DATA(5):1]

[  0.006] send HEADERS frame <length=65, flags=0x05, stream_id=1>

          ; END_STREAM | END_HEADERS

          (padlen=0)

          ; Open new stream

          :authority: 127.0.0.1:8080

          :method: GET

          :path: /upstream/upstream.html

          :scheme: http

          accept: */*

          accept-encoding: gzip, deflate

          user-agent: nghttp2/0.4.0-DEV

[  0.009] recv SETTINGS frame <length=5, flags=0x00, stream_id=0>

          (niv=1)

          [SETTINGS_MAX_CONCURRENT_STREAMS(3):100]

[  0.009] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>

          ; ACK

          (niv=0)

[  0.009] (stream_id=1, noind=0) :status: 200

[  0.009] (stream_id=1, noind=0) date: Tue, 13 May 2014 07:58:44 GMT

[  0.009] (stream_id=1, noind=0) server: Apache/2.4.7 (Ubuntu)

[  0.010] (stream_id=1, noind=0) last-modified: Sun, 11 May 2014 08:13:49 GMT

[  0.010] (stream_id=1, noind=0) etag: “e-4f91b691fc8b7”

[  0.010] (stream_id=1, noind=0) accept-ranges: bytes

[  0.010] (stream_id=1, noind=0) content-length: 14

[  0.010] (stream_id=1, noind=0) content-type: text/html

[  0.010] recv HEADERS frame <length=102, flags=0x04, stream_id=1>

          ; END_HEADERS

          (padlen=0)

          ; First response header

upstream dayo

[  0.011] recv DATA frame <length=14, flags=0x00, stream_id=1>

[  0.011] recv DATA frame <length=0, flags=0x01, stream_id=1>

          ; END_STREAM

[  0.011] send SETTINGS frame <length=0, flags=0x01, stream_id=0>

          ; ACK

          (niv=0)

[  0.011] send GOAWAY frame <length=8, flags=0x00, stream_id=0>

          (last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[])

[/program]

ヘッダ情報をクライアント側から渡したり、リバースプロキシとして返すべきヘッダの整理は未完成ですが、とりあえずHTTP/1のバックエンドサーバが返してくるHeaderやBodyはHTTP/2用にコンバートしてHTTP/2クライアントに返せているのがわかります。HTTP/2で既存のHTTP/1のコンテンツを使えるのはやはり嬉しいものですね。

コンテンツとしてmrubyスクリプトを実行できる機能

続いて、HTTP/2サーバ上でもmrubyスクリプト(mrubyで動くRubyスクリプト)を動的コンテンツとして扱えるようにしました。

この設定は以下のように書きます。

[program lang=’ruby’ escaped=’true’]

s.set_map_to_strage_cb {

  if s.request.filename =~ /^.*\.rb$/

    s.enable_mruby                     

  end

}

[/program]

このように書くと、.rbファイルにアクセスがあった場合はmrubyとして実行して、レスポンスを返します。例えばhello worldは以下のように書きます。

まずは、hello.rbを以下のように用意して、

[program lang=’ruby’ escaped=’true’]

time = Time.now

rputs “Now: #{time}\n”

rputs “hello world”

[/program]

そして、nghttpでTrusterdにhello.rbに対してアクセスします。

すると以下のようにレスポンスが返って来ます。

[program lang=’bash’ escaped=’true’]

$ nghttp -v http://127.0.0.1:8080/hello.rb

[  0.000] send SETTINGS frame <length=15, flags=0x00, stream_id=0>

          (niv=3)

          [SETTINGS_MAX_CONCURRENT_STREAMS(3):100]

          [SETTINGS_INITIAL_WINDOW_SIZE(4):65535]

          [SETTINGS_COMPRESS_DATA(5):1]

[  0.002] send HEADERS frame <length=55, flags=0x05, stream_id=1>

          ; END_STREAM | END_HEADERS

          (padlen=0)

          ; Open new stream

          :authority: 127.0.0.1:8080

          :method: GET

          :path: /hello.rb

          :scheme: http

          accept: */*

          accept-encoding: gzip, deflate

          user-agent: nghttp2/0.4.0-DEV

[  0.011] recv SETTINGS frame <length=5, flags=0x00, stream_id=0>

          (niv=1)

          [SETTINGS_MAX_CONCURRENT_STREAMS(3):100]

[  0.011] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>

          ; ACK

          (niv=0)

[  0.011] (stream_id=1, noind=0) :status: 200

[  0.011] (stream_id=1, noind=0) server: Trusterd/0.0.1

[  0.011] (stream_id=1, noind=0) date: Tue, 13 May 2014 08:21:02 GMT

[  0.011] (stream_id=1, noind=0) last-modified: Tue, 13 May 2014 08:20:58 GMT

[  0.011] recv HEADERS frame <length=57, flags=0x04, stream_id=1>

          ; END_HEADERS

          (padlen=0)

          ; First response header

Now: Tue May 13 01:21:02 2014

hello world

[  0.011] recv DATA frame <length=42, flags=0x00, stream_id=1>

[  0.011] recv DATA frame <length=0, flags=0x01, stream_id=1>

          ; END_STREAM

[  0.011] send SETTINGS frame <length=0, flags=0x01, stream_id=0>

          ; ACK

          (niv=0)

[  0.011] send GOAWAY frame <length=8, flags=0x00, stream_id=0>

          (last_stream_id=0, error_code=NO_ERROR(0), opaque_data(0)=[])

[/program]

時間やhello world文字列のレスポンスが返って来ました。これで一応はTrusterd HTTP/2 Web Serverは動的コンテンツにも対応したことになりますね!

このあたりの実装も、まずは動く事重視で最適化はまだきちんとされていないので、これから詰めていこうと思います。

まとめ

以上のように、最近子育てをしながら合間をみつけてやっていたTrusterdの実装の進捗を書いてみました。まだまだやることや考える事、さらには実装において勉強しないといけない事は沢山ですが、実装するにつれ今まで分かっていなかった事がわかったりと、とても勉強になっています。思考錯誤しながら実装しており、コードもいったりきたりな内容になってきていますが、そのあたりも整理しながら実装していきたいです。

もし興味を持った方がおられたら、一緒にTrusterdを色々実装しながら勉強しつつ、最終的にはTrusterdをそれなりに良いHTTP/2 Web Serverにしてみませんか?

どうぞよろしくお願いします。