mrubyでSPDYやHTTP2通信

年末にSPDYやHTTP2の勉強がてら、mrubyでSPDYやHTTP2通信可能なmrbgemを作りました。

これで、mrubyを組み込んだアプリケーションやデバイスからSPDY・HTTP2通信ができるようになって未来が広がりますね。

現状はクライアントのGETのみができます。これで、例えばmod_mrubyやngx_mrubyに組み込んで、あるURLにアクセスした場合はバックエンドでSPDYやHTTP2な通信をすることもできますね。

サンプル

SPDYやHTTP2通信をするためにはRubyで以下のように書きます。

SPDY通信

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

r = SPDY::Client.get 'https://www.google.co.jp/'

p r
p r.body
p r.body_length
p r.spdy_version
p r.stream_id
p r.syn_reply
p r.syn_stream

[/program]

以下のようなレスポンスが得られます。

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

#r
{
    :syn_reply=>{
        "x-frame-options"=>"SAMEORIGIN", 
        "x-xss-protection"=>"1; mode=block", 
        "expires"=>"-1", 
        "p3p"=>"CP=¥"This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info.¥"", 
        "server"=>"gws", 
        "set-cookie"=>"; expires=Sun, 29-Jun-2014 17:16:55 GMT; path=/; domain=.google.co.jp; HttpOnly", 
        ":version"=>"HTTP/1.1", 
        "alternate-protocol"=>"443:quic", 
        "cache-control"=>"private, max-age=0", 
        "content-type"=>"text/html; charset=Shift_JIS", 
        "date"=>"Sat, 28 Dec 2013 17:16:55 GMT", 
        ":status"=>"200 OK"
    }, 
    :recieve_bytes=>953.0, 
    :body=>"<html> - (snip) - </html>", 
    :body_length=>953, 
    :spdy_proto_version=>4, 
    :stream_id=>1, 
    :syn_stream=>{
        ":method"=>"GET", 
        ":path"=>"/", 
        ":scheme"=>"https", 
        ":version"=>"HTTP/1.1", 
        ":host"=>"www.google.co.jp", 
        "accept"=>"*/*", 
        "user-agent"=>"mruby-spdy/0.0.1"
    }
}

#r.body
"<html> - (snip) - </html>"

#r.body_length
953

#r.spdy_version
4

#r.syn_stream
{
  ":method"=>"GET",
  ":path"=>"/",
  ":scheme"=>"https",
  ":version"=>"HTTP/1.1",
  ":host"=>"www.google.co.jp",
  "accept"=>"*/*",
  "user-agent"=>"mruby-spdy/0.0.1"
}

#r.syn_reply
{
  "x-frame-options"=>"SAMEORIGIN",
  "x-xss-protection"=>"1; mode=block",
  "expires"=>"-1",
  "p3p"=>"CP=¥"This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info.¥"",
  "server"=>"gws",
  "set-cookie"=>"; expires=Sun, 29-Jun-2014 17:16:55 GMT; path=/; domain=.google.co.jp; HttpOnly",
  ":version"=>"HTTP/1.1",
  "alternate-protocol"=>"443:quic",
  "cache-control"=>"private, max-age=0",
  "content-type"=>"text/html; charset=Shift_JIS",
  "date"=>"Sat, 28 Dec 2013 17:16:55 GMT",
  ":status"=>"200 OK"
}

[/program]

HTTP2通信

コードは以下になります。

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

r = HTTP2::Client.get 'https://106.186.112.116/'

p r
p r.body
p r.request_headers
p r.response_headers
p r.status
p r.body
p r.body_length
p r.stream_id

[/program]

レスポンスは以下のようにハッシュで得られます。

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

#r
{
  :body=>"---- snip ----", 
  :body_length=>1400, 
  :stream_id=>1, 
  :frame_send_header_goway=>true, 
  :recieve_bytes=>1400.0, 
  :request_headers=>{
    ":path"=>"/", 
    ":scheme"=>"https", 
    "user-agent"=>"mruby-http2/0.0.1", 
    ":authority"=>"106.186.112.116", 
    ":method"=>"GET", 
    "accept"=>"*/*"
  }, 
  :response_headers=>{
    "last-modified"=>"Wed, 18 Dec 2013 15:12:23 GMT", 
    "etag"=>"¥"52b1bb57-2450¥"", 
    "x-varnish"=>"340171131", 
    "content-length"=>"9296", 
    "date"=>"Tue, 31 Dec 2013 11:04:13 GMT", 
    "age"=>"0", 
    "accept-ranges"=>"bytes", 
    "content-type"=>"text/html", 
    ":status"=>"200", 
    "server"=>"nginx/1.4.1 (Ubuntu)", 
    "via"=>"1.1 varnish, 1.1 nghttpx"
  }
}

#r,status
200

#r.request_headers
{
  ":path"=>"/",
  ":scheme"=>"https",
  "user-agent"=>"mruby-http2/0.0.1",
  ":authority"=>"106.186.112.116",
  ":method"=>"GET",
  "accept"=>"*/*"
}

#r.response_headers
{
  "last-modified"=>"Wed, 18 Dec 2013 15:12:23 GMT",
  "etag"=>"¥"52b1bb57-2450¥"",
  "x-varnish"=>"340171131",
  "content-length"=>"9296",
  "date"=>"Tue, 31 Dec 2013 11:04:13 GMT",
  "age"=>"0",
  "accept-ranges"=>"bytes",
  "content-type"=>"text/html",
  ":status"=>"200",
  "server"=>"nginx/1.4.1 (Ubuntu)",
  "via"=>"1.1 varnish, 1.1 nghttpx"
}

#r.body

[/program]

まとめ

このように簡単にmrubyでSPDY・HTTP2でGETが簡単にできるようになりました。是非遊んでみて下さい。今後やりたいこととしては、

  • 通信中のコールバック関数をRubyで書けるようにする
  • サーバ・プロキシ実装
  • クライアント・サーバ・プロキシの細かい機能を追加
  • httpパーサやsslをmrubyのクラスに分離

を考えています。また、pull-requestも大歓迎であります。