Rack 触ってみた
 本当に触ってみただけ
  @hamajyotan
アジェンダ
     3本立て
       近況報告
       Rack ことはじめ, 触ってみる (Main)
       触って困ったところ少し

     雑談, 質疑応答




01                                34
いまここ
     3本立て
       近況報告 <== いまここ
       Rack ことはじめ, 触ってみる
       触って困ったところ少し

     雑談, 質疑応答




02                         34
近況報告
     まゆしぃ
       トゥットゥルー, まだ数話しかみてない

     野球で西武がビリでびっくりした
       野球には全く興味がない
       自分が知っている西武は常勝チームだった

     室伏さんマジかっこいい
       室伏になら投げられてもいい



03                           34
いまここ
     3本立て
       近況報告
       Rack ことはじめ, 触ってみる <== いまここ
       触って困ったところ少し

     雑談, 質疑応答




04                                  34
ことはじめ, 触ってみる
     Rack って何?
        ちょっとググればわかることを得意げに話します
           概要とか
           仕組みとか

     触ってみる
        触ってみます




05                               34
Rack とは
     Web サーバと Web アプリの架け橋, グルー, I/F
       色んな Web サーバ
          webrick, thin, mongrel, passenger, unicorn, ..
       色んな Web アプリ (framework)
          rails, sinatra, camping, ...
       Web サーバとアプリケーションの間のお約束




06                                                         34
どんなお約束なの ?
     #call ってメッセージに反応するなにか
       1つの引数を受け付ける
         リクエストを意味する Hash
       3つの要素を持つ Array を返す
         ステータスコード
         レスポンスヘッダを意味する Hash
         レスポンスボディを意味する Array




07                             34
つまりこんな感じ
     Proc.new { |env|
       [
         200,
         {'Content-Type' => 'text/html'},
         ['Hello, World!']
       ]
     }



08                                          34
config.ru
     require 'rubygems'
     require 'rack'
     app = Proc.new { |env|
       [
         200,
         {'Content-Type' => 'text/html'},
         ['Hello, World!']
       ]
     }
09   run app                                34
実行してみる
     $ rackup
     [2011-08-30 11:18:31] INFO WEBrick 1.3.1
     [2011-08-30 11:18:31] INFO ruby 1.9.2 (2011-02-18) [i686-linux]
     [2011-08-30 11:18:31] INFO WEBrick::HTTPServer#start: pid=8494 port=9292




10                                                                              34
リクエストしてみる
     $ telnet 127.0.0.1 9292
     Trying 127.0.0.1...
     Connected to 127.0.0.1.
     Escape character is '^]'.
     GET / HTTP/1.1

     HTTP/1.1 200 OK
     Content-Type: text/plain
     Transfer-Encoding: chunked
     Server: WEBrick/1.3.1 (Ruby/1.9.2/2011-02-18)
     Date: Tue, 30 Aug 2011 02:09:49 GMT
     Content-Length: 23
     Connection: Keep-Alive

     d
     Hello, World!
     0

     Connection closed by foreign host.
11                                                   34
反応してる
     $ rackup
     [2011-08-30   11:18:31] INFO    WEBrick 1.3.1
     [2011-08-30   11:18:31] INFO    ruby 1.9.2 (2011-02-18) [i686-linux]
     [2011-08-30   11:18:31] INFO    WEBrick::HTTPServer#start: pid=8494 port=9292
     127.0.0.1 -   - [30/Aug/2011   11:18:40] "GET / HTTP/1.1" 200 - 0.0012




12                                                                                   34
mongrel で動かすには ?
       $ rackup -s mongrel




13                           34
passenger では ?
     config.ru 以外に少し必要
        public/ ディレクトリ (空っぽでOK)
        (任意) tmp/restart.txt
            apache を再起動せずにアプリを再起動できる




14                                     34
pasenger で動かす-1
     $ tree /home/hamajyotan/rack/test
     /home/hamajyotan/rack/test
     ├── config.ru
     ├── public
     └── tmp
         └── restart.txt


15                                       34
passenger で動かす-2
     /home/hamajyotan/rack/test/ に配置されている前提で・・

        <VirtualHost *:80>
          RackEnv production
          ServerName www.yourhost.com
          DocumentRoot /home/hamajyotan/rack/test/public
          <Directory /home/hamajyotan/rack/test/public>
            AllowOverride all
            Options -MultiViews
          </Directory>
        </VirtualHost>



16                                                         34
env って何が入っているの?
     # config.ru
     require 'rack'
     require 'yaml'
     run Proc.new { |env|
       [
         200,
         {'Content-Type' => 'text/plain',
         [env.to_yaml],
       ]
     }
17                                          34
env の中身
     ---
     GATEWAY_INTERFACE: CGI/1.1
     PATH_INFO: /
     QUERY_STRING: ""
     REMOTE_ADDR: 127.0.0.1
     REMOTE_HOST: localhost
     REQUEST_METHOD: GET
     REQUEST_URI: http://localhost:9292/
     SCRIPT_NAME: ""
     SERVER_NAME: localhost
     SERVER_PORT: "9292"
     SERVER_PROTOCOL: HTTP/1.1
     SERVER_SOFTWARE: WEBrick/1.3.1 (Ruby/1.9.2/2011-07-09)
     HTTP_HOST: localhost:9292
     HTTP_CONNECTION: keep-alive
     HTTP_USER_AGENT: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.218 Safari/535.1
     HTTP_ACCEPT: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
     HTTP_ACCEPT_ENCODING: gzip,deflate,sdch
     HTTP_ACCEPT_LANGUAGE: ja,en-US;q=0.8,en;q=0.6
     HTTP_ACCEPT_CHARSET: Shift_JIS,utf-8;q=0.7,*;q=0.3
     rack.version:
     - 1
     - 1
     rack.input: !ruby/object:Rack::Lint::InputWrapper
       input: !ruby/object:StringIO {}
     rack.errors: !ruby/object:Rack::Lint::ErrorWrapper
       error: !ruby/object:IO {}
     rack.multithread: true
     rack.multiprocess: false
     rack.run_once: false
     rack.url_scheme: http
     HTTP_VERSION: HTTP/1.1
     REQUEST_PATH: /
18                                                                                                                          34
ここまでのまとめ
     rack 仕様に準拠したコード
       #call - リクエストを引数に、レスポンスを返す関数
       config.ru ってファイルを準備
           #call に反応するインスタンスを run に与える

     rack の仕様に準拠したサーバならどこでも動く
       webrick, mongrel, passenger, ...

     env の中にはリクエストに関する情報がある
       path とか port とか useragent とか
       method とか body とか
19                                        34
Rack ミドルウェア
     基本は一緒
       #call ってメッセージに応答
       { before | after } フィルタ

     他の Rack アプリを内包する
       デザパタで言うところの Decorator パターン




20                                  34
コードでおk?
     class Middleware
       def initialize app
         @app = app
       end

       def call env
         # before...
         code, header, body = @app.call env
         # after...
         [code, header, body]
       end
21   end                                      34
処理後レスポンスヘッダ付加
     class AddServerHeader
       def initialize app
         @app = app
       end
       def call env
         code, header, body = @app.call env
         header['Server'] = "#{header['Server']} (*^o^)=3"
         [code, header, body]
       end
     end




22                                                           34
どうやって使う ?
     # config.ru
     require 'rack'
     require 'add_server_header'

     app = Proc.new { |env|
       [200, {'Content-Type' => 'text/plain'}, ['Hello, World']]
     }

     use AddServerHeader
     run app




23                                                                 34
use がキモ
     # これは
     use AddServerHeader
     run app

     # これと同じ
     middleware = AddServerHeader.new(app)
     run middleware



24                                           34
use がキモ
     # これは
     use AddServerHeader
     use Other
     run app

     # これと同じ
     middleware = AddServerHeader.new(Other.new(app))
     run middleware




25                                                      34
use がキモ
     # これは
     use AddServerHeader
     use AddServerHeader
     run app

     # これと同じ
     middleware = AddServerHeader.new(AddServerHeader.new(app))
     run middleware




26                                                                34
いろんなミドルウェア
     Rack::Auth::Basic
             基本認証を実現

     Rack::Static
             静的ファイルを出力する

     Rack::ConditionalGet
             条件付き GET を実現

     Etc..


27                          34
例えば基本認証
     # config.ru
     require 'rack'
     app = Proc.new { |env|
       [200, {'Content-Type' => 'text/plain'}, ['Hello, World']]
     }
     use Rack::Auth::Basic, 'mayusi-' do |user, pass|
       user == 'mayusi-' && pass == 'mayusi-!'
     end
     run app


     #   ちなみ use 以下はこれと同じ
     #   auth = Rack::Auth::Basic.new(app, 'mayusi-') do |user, pass|
     #     user == 'mayusi-' && pass == 'mayusi-!'
     #   end
     #   run auth


28                                                                      34
ここまでのまとめ
     ミドルウェア
       基本は rack アプリケーションと同じ
         #call を持つ
         env を引数に受け取る
         レスポンスを返す

       他のアプリケーションを Decorator パターンで内包する




29                                       34
いまここ
     3本立て
       近況報告
       Rack ことはじめ, 触ってみる
       触って困ったところ少し <== いまここ

     雑談, 質疑応答




30                            34
困ったところ
     WEBrick
        'Content-Length: 0' をガン無視
               env['CONTENT_LENGTH'] = '0' って設定してくれない
        他の Web サーバはしてくれた

     GET パラメータのセパレータ
        & だけでなく、 ; もセパレータ扱い
        RFC 的に正しいか良く分かっていません



31                                                      34
Paranoia なところ
     HTTP のヘッダ名
        大文字小文字区別しても良いのか否か?
        アンダースコアをヘッダ名にできないけど・・


     x-hoge-hogehoge: fuga
     # => env['HTTP_X_HOGE_HOGEHOGE'] = 'fuga'
     x-hoge_HogeHoge: piyo
     # => env['HTTP_X_HOGE_HOGEHOGE'] = 'piyo'


32                                               34
最後に
     rack を利用したアプリケーション例
       というか宣伝 ?
       https://github.com/hamajyotan/castoro-s3-adapter
       castoro-s3-adapter ってのを作成中です
       castoro をバックエンドにした Amazon s3 実装
       というか castoro を S3 プロトコルでアクセス可能にするも
       の




33                                                        34
おわりです
     ご清聴有難うございます
     質問ありましたらどうぞ
     雑談でもおk




34                    34

Tottoruby 20110903

  • 1.
  • 2.
    アジェンダ 3本立て 近況報告 Rack ことはじめ, 触ってみる (Main) 触って困ったところ少し 雑談, 質疑応答 01 34
  • 3.
    いまここ 3本立て 近況報告 <== いまここ Rack ことはじめ, 触ってみる 触って困ったところ少し 雑談, 質疑応答 02 34
  • 4.
    近況報告 まゆしぃ トゥットゥルー, まだ数話しかみてない 野球で西武がビリでびっくりした 野球には全く興味がない 自分が知っている西武は常勝チームだった 室伏さんマジかっこいい 室伏になら投げられてもいい 03 34
  • 5.
    いまここ 3本立て 近況報告 Rack ことはじめ, 触ってみる <== いまここ 触って困ったところ少し 雑談, 質疑応答 04 34
  • 6.
    ことはじめ, 触ってみる Rack って何? ちょっとググればわかることを得意げに話します 概要とか 仕組みとか 触ってみる 触ってみます 05 34
  • 7.
    Rack とは Web サーバと Web アプリの架け橋, グルー, I/F 色んな Web サーバ webrick, thin, mongrel, passenger, unicorn, .. 色んな Web アプリ (framework) rails, sinatra, camping, ... Web サーバとアプリケーションの間のお約束 06 34
  • 8.
    どんなお約束なの ? #call ってメッセージに反応するなにか 1つの引数を受け付ける リクエストを意味する Hash 3つの要素を持つ Array を返す ステータスコード レスポンスヘッダを意味する Hash レスポンスボディを意味する Array 07 34
  • 9.
    つまりこんな感じ Proc.new { |env| [ 200, {'Content-Type' => 'text/html'}, ['Hello, World!'] ] } 08 34
  • 10.
    config.ru require 'rubygems' require 'rack' app = Proc.new { |env| [ 200, {'Content-Type' => 'text/html'}, ['Hello, World!'] ] } 09 run app 34
  • 11.
    実行してみる $ rackup [2011-08-30 11:18:31] INFO WEBrick 1.3.1 [2011-08-30 11:18:31] INFO ruby 1.9.2 (2011-02-18) [i686-linux] [2011-08-30 11:18:31] INFO WEBrick::HTTPServer#start: pid=8494 port=9292 10 34
  • 12.
    リクエストしてみる $ telnet 127.0.0.1 9292 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. GET / HTTP/1.1 HTTP/1.1 200 OK Content-Type: text/plain Transfer-Encoding: chunked Server: WEBrick/1.3.1 (Ruby/1.9.2/2011-02-18) Date: Tue, 30 Aug 2011 02:09:49 GMT Content-Length: 23 Connection: Keep-Alive d Hello, World! 0 Connection closed by foreign host. 11 34
  • 13.
    反応してる $ rackup [2011-08-30 11:18:31] INFO WEBrick 1.3.1 [2011-08-30 11:18:31] INFO ruby 1.9.2 (2011-02-18) [i686-linux] [2011-08-30 11:18:31] INFO WEBrick::HTTPServer#start: pid=8494 port=9292 127.0.0.1 - - [30/Aug/2011 11:18:40] "GET / HTTP/1.1" 200 - 0.0012 12 34
  • 14.
    mongrel で動かすには ? $ rackup -s mongrel 13 34
  • 15.
    passenger では ? config.ru 以外に少し必要 public/ ディレクトリ (空っぽでOK) (任意) tmp/restart.txt apache を再起動せずにアプリを再起動できる 14 34
  • 16.
    pasenger で動かす-1 $ tree /home/hamajyotan/rack/test /home/hamajyotan/rack/test ├── config.ru ├── public └── tmp └── restart.txt 15 34
  • 17.
    passenger で動かす-2 /home/hamajyotan/rack/test/ に配置されている前提で・・ <VirtualHost *:80> RackEnv production ServerName www.yourhost.com DocumentRoot /home/hamajyotan/rack/test/public <Directory /home/hamajyotan/rack/test/public> AllowOverride all Options -MultiViews </Directory> </VirtualHost> 16 34
  • 18.
    env って何が入っているの? # config.ru require 'rack' require 'yaml' run Proc.new { |env| [ 200, {'Content-Type' => 'text/plain', [env.to_yaml], ] } 17 34
  • 19.
    env の中身 --- GATEWAY_INTERFACE: CGI/1.1 PATH_INFO: / QUERY_STRING: "" REMOTE_ADDR: 127.0.0.1 REMOTE_HOST: localhost REQUEST_METHOD: GET REQUEST_URI: http://localhost:9292/ SCRIPT_NAME: "" SERVER_NAME: localhost SERVER_PORT: "9292" SERVER_PROTOCOL: HTTP/1.1 SERVER_SOFTWARE: WEBrick/1.3.1 (Ruby/1.9.2/2011-07-09) HTTP_HOST: localhost:9292 HTTP_CONNECTION: keep-alive HTTP_USER_AGENT: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.218 Safari/535.1 HTTP_ACCEPT: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 HTTP_ACCEPT_ENCODING: gzip,deflate,sdch HTTP_ACCEPT_LANGUAGE: ja,en-US;q=0.8,en;q=0.6 HTTP_ACCEPT_CHARSET: Shift_JIS,utf-8;q=0.7,*;q=0.3 rack.version: - 1 - 1 rack.input: !ruby/object:Rack::Lint::InputWrapper input: !ruby/object:StringIO {} rack.errors: !ruby/object:Rack::Lint::ErrorWrapper error: !ruby/object:IO {} rack.multithread: true rack.multiprocess: false rack.run_once: false rack.url_scheme: http HTTP_VERSION: HTTP/1.1 REQUEST_PATH: / 18 34
  • 20.
    ここまでのまとめ rack 仕様に準拠したコード #call - リクエストを引数に、レスポンスを返す関数 config.ru ってファイルを準備 #call に反応するインスタンスを run に与える rack の仕様に準拠したサーバならどこでも動く webrick, mongrel, passenger, ... env の中にはリクエストに関する情報がある path とか port とか useragent とか method とか body とか 19 34
  • 21.
    Rack ミドルウェア 基本は一緒 #call ってメッセージに応答 { before | after } フィルタ 他の Rack アプリを内包する デザパタで言うところの Decorator パターン 20 34
  • 22.
    コードでおk? class Middleware def initialize app @app = app end def call env # before... code, header, body = @app.call env # after... [code, header, body] end 21 end 34
  • 23.
    処理後レスポンスヘッダ付加 class AddServerHeader def initialize app @app = app end def call env code, header, body = @app.call env header['Server'] = "#{header['Server']} (*^o^)=3" [code, header, body] end end 22 34
  • 24.
    どうやって使う ? # config.ru require 'rack' require 'add_server_header' app = Proc.new { |env| [200, {'Content-Type' => 'text/plain'}, ['Hello, World']] } use AddServerHeader run app 23 34
  • 25.
    use がキモ # これは use AddServerHeader run app # これと同じ middleware = AddServerHeader.new(app) run middleware 24 34
  • 26.
    use がキモ # これは use AddServerHeader use Other run app # これと同じ middleware = AddServerHeader.new(Other.new(app)) run middleware 25 34
  • 27.
    use がキモ # これは use AddServerHeader use AddServerHeader run app # これと同じ middleware = AddServerHeader.new(AddServerHeader.new(app)) run middleware 26 34
  • 28.
    いろんなミドルウェア Rack::Auth::Basic 基本認証を実現 Rack::Static 静的ファイルを出力する Rack::ConditionalGet 条件付き GET を実現 Etc.. 27 34
  • 29.
    例えば基本認証 # config.ru require 'rack' app = Proc.new { |env| [200, {'Content-Type' => 'text/plain'}, ['Hello, World']] } use Rack::Auth::Basic, 'mayusi-' do |user, pass| user == 'mayusi-' && pass == 'mayusi-!' end run app # ちなみ use 以下はこれと同じ # auth = Rack::Auth::Basic.new(app, 'mayusi-') do |user, pass| # user == 'mayusi-' && pass == 'mayusi-!' # end # run auth 28 34
  • 30.
    ここまでのまとめ ミドルウェア 基本は rack アプリケーションと同じ #call を持つ env を引数に受け取る レスポンスを返す 他のアプリケーションを Decorator パターンで内包する 29 34
  • 31.
    いまここ 3本立て 近況報告 Rack ことはじめ, 触ってみる 触って困ったところ少し <== いまここ 雑談, 質疑応答 30 34
  • 32.
    困ったところ WEBrick 'Content-Length: 0' をガン無視 env['CONTENT_LENGTH'] = '0' って設定してくれない 他の Web サーバはしてくれた GET パラメータのセパレータ & だけでなく、 ; もセパレータ扱い RFC 的に正しいか良く分かっていません 31 34
  • 33.
    Paranoia なところ HTTP のヘッダ名 大文字小文字区別しても良いのか否か? アンダースコアをヘッダ名にできないけど・・ x-hoge-hogehoge: fuga # => env['HTTP_X_HOGE_HOGEHOGE'] = 'fuga' x-hoge_HogeHoge: piyo # => env['HTTP_X_HOGE_HOGEHOGE'] = 'piyo' 32 34
  • 34.
    最後に rack を利用したアプリケーション例 というか宣伝 ? https://github.com/hamajyotan/castoro-s3-adapter castoro-s3-adapter ってのを作成中です castoro をバックエンドにした Amazon s3 実装 というか castoro を S3 プロトコルでアクセス可能にするも の 33 34
  • 35.
    おわりです ご清聴有難うございます 質問ありましたらどうぞ 雑談でもおk 34 34