読者です 読者をやめる 読者になる 読者になる

人間とウェブの未来

「ウェブの歴史は人類の歴史の繰り返し」という観点から色々勉強しています。

ngx_mruby経由でtrusterdをnginxに組み込んでnginxの情報をHTTP/2で裏から取得してみた

trusterdはHTTP/2クライアントとサーバ両方の機能を持っており、それらの機能は全てモジュールとして取り外し可能な設計にしています。

そのため、HTTP/2クライアント・サーバ機能をmrubyモジュールとして取り外して、他のアプリケーションにmruby経由で組み込めば、簡単にtrusterdのHTTP/2機能を利用することができます。

今回は、遊びとちょっとの真面目さで、ngx_mruby経由でnginxにtrusterdのHTTP/2サーバ機能を組込み、nginx起動時にnginxの内部情報HTTP/2サーバをforkして起動し、HTTP/2クライアントからnginxのバージョン情報を取り出してみました。

何いってるの?という感じですが、最後までお付き合い下さい。

ngx_mrubyにmruby-http2を組込み

trusterdのHTTP/2機能はmruby-http2として利用可能です。ですので、以下のようにngx_mrubyのbuild_conig.rbにモジュールを組み込む設定を書きましょう。下記を追記します。

  conf.gem :github => 'matsumoto-r/mruby-simplehttp'                             
  conf.gem :github => 'mattn/mruby-http'                                         
  conf.gem :github => 'trusterd/mruby-http2'                                     

そしていつも通りngx_mrubyをビルドしてngx_mrubyを組み込んだnginxを作成します。簡単ですね。

nginx内部用HTTP/2サーバを起動する設定をnginx.confに書く

続いて、nginx.confに設定を書きます。

今回はworker起動時のフックであるmruby_init_worker_codeに以下のように書きました。関連箇所だけ書きます。

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on; 
    keepalive_timeout  65; 

    mruby_init_worker_code '

      root_dir = "/usr/local/trusterd"
      
      tls = HTTP2::Server.new({
      
        :port           => 8080,
        :document_root  => "#{root_dir}/htdocs",
        :server_name    => "nginx_internal_http2",
      
        :worker         => "auto",
      
        :key            => "#{root_dir}/ssl/server.key",
        :crt            => "#{root_dir}/ssl/server.crt",
      
        :callback       => true,
      
      })  

      # レスポンス生成処理でフックしてレスポンスデータをここで作成
      tls.set_content_cb {
        tls.r.rputs Nginx.nginx_version
      }   

      pid1 = Process.fork() { tls.run }
    ';  

    server {
        listen       8079;
        server_name  localhost;

・
・
・

これで、nginx起動時にtls使用のnginx_internal_http2というHTTP/2 Webサーバもforkして同時に起動することができそうです。

そして、HTTP/2リクエストを送ると、Nginx.nginx_versionというngx_mrubyのクラス・メソッドによってnginxのバージョン情報を得られそうですね。

起動させてレスポンスを確認する

では起動させてみましょう。

以下のようなプロセスの状態になりました。

matsumo+ 48189  0.5  0.0  36540  3756 pts/8    S+   23:28   0:00  |   \_ ./build/nginx/sbin/nginx
matsumo+ 48190  0.0  0.0  36540  1644 pts/8    S+   23:28   0:00  |       \_ ./build/nginx/sbin/nginx
matsumo+ 48191  0.0  0.0  36672  3080 pts/8    S+   23:28   0:00  |           \_ ./build/nginx/sbin/nginx
matsumo+ 48192  0.0  0.0  36672  3080 pts/8    S+   23:28   0:00  |           \_ ./build/nginx/sbin/nginx
matsumo+ 48193  0.0  0.0  36672  3064 pts/8    S+   23:28   0:00  |           \_ ./build/nginx/sbin/nginx
matsumo+ 48194  0.0  0.0  36672  3068 pts/8    S+   23:28   0:00  |           \_ ./build/nginx/sbin/nginx

HTTP/2サーバがforkされて、今回はworker数を適当にautoにしたのでCPUコアの数だけ起動していますね。

では、外部からこのHTTP/2サーバにアクセスしてみましょう。

$ nghttp https://127.0.0.1:8080/
1.7.7

起動中のnginxのバージョンを裏からHTTP/2経由で取得できました。

$ nghttp -v https://127.0.0.1:8080/ | grep server
[  0.008][NPN] server offers:
[  0.015] recv (stream_id=1, noind=0) server: nginx_internal_http2

serverヘッダもnginx_internal_http2になっています。

まとめ

ということで、今回はtrusterdのHTTP/2機能を取り外した上で、別のアプリに組み込んでHTTP/2機能を試す事に成功しました。また、アプリの内部の情報をHTTP/2で裏から取ることもできました。

今回はほぼ遊びですが、なんとなくもっとうまくやれば面白い事ができるんじゃないかという実感を得ました(得た気がしました)。

この機能を応用すると、ミドルウェア間での通信やデバイス間での通信などにHTTP/2を使って協調して動くような処理が可能かもしれませんね。

是非遊んでみてください。