Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Toycol: Define your own application protocol

Toycol: Define your own application protocol

More Decks by Misaki Shioi(塩井美咲/しおい)

Other Decks in Programming

Transcript

  1. .JTBLJ4IJPJ [email protected]

    4FQ
    3VCZ,BJHJ5BLFPVU
    5PZDPM
    %F
    fi
    OFZPVSPXOBQQMJDBUJPOQSPUPDPM

    View Slide

  2. "CPVUNF
    (JU)VC!TIJPJNN
    [email protected]
    8FCBQQMJDBUJPOEFWFMPQFSBU$BUBM *OD
    .FNCFSPG"TBLVTBSC'VLVPLBSC

    View Slide

  3. "CPVUNF
    IUUQTSVCZLBJHJPSH
    ʜBOEXBTBIFMQFSTUB
    ff

    BU3VCZ,BJHJJO'VLVPLB

    View Slide

  4. *`NWFSZIBQQZUPCFBTQFBLFSBU
    3VCZ,BJHJ5BLFPVU

    View Slide

  5. #ZUIFXBZ

    View Slide

  6. 8IBUEPZPVMJLFBCPVU
    5$1*1QSPUPDPMTUBDL
    *ONZPQJOJPOʜ

    View Slide

  7. 5IFGBDUUIBUFBDIMBZFS
    EPFTJUTPXOKPC
    5IFZEPOUDBSFXIBU
    PUIFSMBZFSTBSFEPJOH
    Application Layer
    Transport Layer
    Internet Layer
    Link Layer
    8IBU*MJLFBCPVU5$1*1QSPUPDPMTUBDLᶃ
    5$1*1QSPUPDPMTUBDL

    View Slide

  8. 8IBU*MJLFBCPVU5$1*1QSPUPDPMTUBDLᶄ
    /FUXPSLOPEF
    Application Layer
    Transport Layer
    Internet Layer
    Link Layer
    /FUXPSLOPEF
    Application Layer
    Transport Layer
    Internet Layer
    Link Layer

    5IFGBDUUIBUMBZFSTPGUIFTBNFMFWFMPGUIFBCTUSBDUJPO
    DPNNVOJDBUFXJUIFBDIPUIFSVTJOHUIFTBNFQSPUPDPM

    View Slide

  9. 4P XIBUJTB
    JOUIFDPOUFYUPGDPNQVUFSOFUXPSL
    QSPUPDPM

    View Slide

  10. *UJTBTFUPGSVMFTGPSDPNNVOJDBUJPO
    CFUXFFOOFUXPSLOPEFT

    View Slide

  11. 'PSFYBNQMF
    $MJFOU/PEF 4FSWFS/PEF
    0O888 BDMJFOUBOEBTFSWFSBSFUSBOTNJUUJOH
    IZQFSNFEJBEPDVNFOUTVTJOH)551QSPUPDPM
    World Wide Web
    Transport Layer
    Internet Layer
    Link Layer
    World Wide Web
    Transport Layer
    Internet Layer
    Link Layer
    )551

    View Slide

  12. 'PSFYBNQMF
    $MJFOU/PEF 4FSWFS/PEF
    #VUUIFMBZFSTCFMPXUIFUSBOTQPSUMBZFSEPO`ULOPX
    XIBUUIFQSPUPDPMJTMJLF888JTVTJOH
    World Wide Web
    Transport Layer
    Internet Layer
    Link Layer
    World Wide Web
    Transport Layer
    Internet Layer
    Link Layer
    ˕

    View Slide

  13. 'PSFYBNQMF
    $MJFOU/PEF 4FSWFS/PEF
    4UJMM UIFMBZFSTCFMPXUIFUSBOTQPSUMBZFSDBOUSBOTGFS
    XIBUFWFSEBUB888VTFT
    World Wide Web
    Transport Layer
    Internet Layer
    Link Layer
    World Wide Web
    Transport Layer
    Internet Layer
    Link Layer
    )551 )551

    View Slide

  14. 'PSFYBNQMF
    $MJFOU/PEF 4FSWFS/PEF
    "HBJO UIFOFUXPSLOPEFTDBODPNNVOJDBUFXJUIFBDIPUIFS
    BTMPOHBTUIFZCPUIVOEFSTUBOEUIFTBNFQSPUPDPM
    World Wide Web
    Transport Layer
    Internet Layer
    Link Layer
    World Wide Web
    Transport Layer
    Internet Layer
    Link Layer
    )551

    View Slide

  15. 'PSFYBNQMF
    $MJFOU/PEF 4FSWFS/PEF
    *OPUIFSXPSET UIFQSPUPDPMGPSDPNNVOJDBUJPOEPFTOU
    IBWFUPCF)551
    BTMPOHBTUIFZVOEFSTUBOEJU
    World Wide Web
    Transport Layer
    Internet Layer
    Link Layer
    World Wide Web
    Transport Layer
    Internet Layer
    Link Layer
    ˕

    View Slide

  16. 4P
    *IBWFDSFBUFEBTNBMMGSBNFXPSL
    UIBUBMMPXTZPVUPDSFBUF
    ZPVSPXOQSPUPDPMGPSDPNNVOJDBUJPO
    CFUXFFOBDMJFOUBOEBTFSWFSPO888
    JOTUFBEPG)551

    View Slide

  17. *OUIJTGSBNFXPSL
    UIFQSPUPDPMZPVEF
    fi
    OFXPSLTBT
    BXSBQQFSGPSUIF)551
    BOEDBOCFVTFEUPSVOBQQMJDBUJPOT

    View Slide

  18. -FUTTFFIPXJUXPSLT

    View Slide

  19. FH%VDLQSPUPDPM

    View Slide

  20. FH%VDLQSPUPDPM
    RVBDL [email protected]
    *OUIJTQSPUPDPM BDMJFOUTQFBLTUPBTFSWFSXJUI
    RVBDL [email protected]
    5IFOUIFTFSWFSVOEFSTUBOETJU
    Client Server

    View Slide

  21. 4FSWFSFYFDVUFTUIFBQQMJDBUJPOBOESFUVSOTUIFSFTVMU
    PGUIFBQQMJDBUJPOFYFDVUJPOUPUIFDMJFOU
    Client Server
    Application
    )5510,ʜ
    FH%VDLQSPUPDPM

    View Slide

  22. *UHPFTMJLFUIJT

    View Slide

  23. $ toycol server
    4UBSUVQBTFSWFSUIBUVOEFSTUBOET%VDLQSPUPDPM
    $ toycol client “quack, quack /posts<3user_id=1”
    4FOESFRVFTUNFTTBHFCZ%VDLQSPUPDPMUPUIFTFSWFS
    FH%VDLQSPUPDPM

    View Slide

  24. $ toycol server

    [Toycol] Start built-in server, listening on unix:///tmp/toycol.socket

    [Toycol] Start proxy server on duck protocol,

    listening on localhost:9292

    => Use Ctrl-C to stop

    [Toycol] Received message: "quack, quack /posts<3user_id=1”

    [Toycol] Message has been translated to HTTP request message:

    "GET /posts? user_id=1 HTTP/1.1\r\nContent-Length: 0\r\n\r\n"

    [Toycol] Successed to Send HTTP request message to server

    [Toycol] Received response message from server:

    HTTP/1.1 200 OK

    [Toycol] Finished to response to client
    -PHTPOUIFTFSWFSTJEF

    View Slide

  25. $ toycol client “quack, quack /posts<3user_id=1”

    [Toycol] Sent request message: quack, quack /posts<3user_id=1

    ---

    [Toycol] Received response message:

    HTTP/1.1 200 OK

    Content-Type: text/html

    Content-Length: 32

    quack quack, I am the No.1 duck
    -PHTPOUIFDMJFOUTJEF

    View Slide

  26. )PXFWFS JOUIFSFBMXPSME
    UIFSFJT ZFU
    OPGVMM
    fl
    FEHFEXFCTFSWFS
    PSCSPXTFSJOUIFXPSMEUIBUSVOT
    POUIFQSPUPDPMZPVEFWJTFE
    4P UIFQSPUPDPMEF
    fi
    OFE
    CZUIJTGSBNFXPSLJTJOGBDUBlUPZz

    View Slide

  27. 'PSUIJTSFBTPO
    *OBNFEUIJTGSBNFXPSLBTGPMMPXT

    View Slide

  28. 5PZ1SPUPDPM
    5PZDPM

    View Slide

  29. 5PZ1SPUPDPM
    5PZDPM
    IUUQTHJUIVCDPNTIJPJNNUPZDPM

    View Slide

  30. *OEFFE 5PZDPMJTBGSBNFXPSLGPSUPZ
    )PXFWFS CZVTJOHUIJTGSBNFXPSL ZPVXJMMCF
    BCMFUPFYQFSJFODFBOEMFBSO
    )PXUIFBQQMJDBUJPOMBZFSJTDPOOFDUFEUP
    UIFUSBOTQPSUMBZFSz
    BOE)PXUIFBQQMJDBUJPOQSPUPDPMXPSLTPO
    UIFUSBOTQPSUMBZFS

    View Slide

  31. 5PEBZ *XPVMEMJLFUPTIBSFJUXJUIZPV

    View Slide

  32. 5PEBZTBHFOEB
    )PXUPVTF5PZDPM
    XJUIBFYBNQMFPGTJNQMFEF
    fi
    OJUJPOT
    *OUFSOBMJNQMFNFOUBUJPOPG5PZDPM
    "EWBODFEQSPUPDPMEF
    fi
    OJUJPOT

    View Slide

  33. )PXUPVTF5PZDPM
    XJUIBFYBNQMFPGTJNQMFEF
    fi
    OJUJPOT

    View Slide

  34. -FU`TEF
    fi
    OFBOFXQSPUPDPMXJUI5PZDPM
    BOETFFIPXJUXPSLTJOQSBDUJDF
    5IJTUJNF MFUTDSFBUF
    BOFXQSPUPDPMOBNFE3VCZMJLF
    XPSLTXJUI3VCZMJLFTZOUBYQSPUPDPM

    View Slide

  35. 5PEF
    fi
    OFBOFXQSPUPDPM ZPVOFFEUPQSFQBSF
    UIJTGPMMPXJOHUXPJUFNT
    "DPO
    fi
    HVSBUJPO
    fi
    MF
    'PSEF
    fi
    OJOHQSPUPDPM
    5IJT
    fi
    MFOFFETUPCFOBNFE
    l1SPUPDPM
    fi
    MFzPSl1SPUPDPM
    fi
    MFSVCZMJLFzBOETPPO
    "3BDLDPNQBUJCMFBQQMJDBUJPO
    FHDPO
    fi
    HSV
    ᶃ1SFQBSFBDPO
    fi
    HVSBUJPO
    fi
    MFBBQQMJDBUJPO

    View Slide

  36. ᶃ1SFQBSFBDPO
    fi
    HVSBUJPO
    fi
    MFBBQQMJDBUJPO
    :PVDBOFBTJMZDSFBUFUIFTFTLFMFUPOTCZVTJOH
    UPZDPMHFOFSBUFDPNNBOE
    $ toycol generate rubylike

    [Toycol] Generate Protocol
    fi
    le.rubylike in /path/to/current_directory

    [Toycol] Generate con
    fi
    g_rubylike.ru in /path/to/current_directory

    :PVDBOQBTTUIFQSPUPDPMOBNF
    BTBOBSHVNFOUUP
    UPZDPMHFOFSBUFDPNNBOE
    6TFS

    View Slide

  37. ᶄ%F
    fi
    OFZPVSQSPUPDPMJOUIF1SPUPDPM
    fi
    MF
    Toycol::Protocol.de
    fi
    ne(:rubylike) do

    request.path do |message|

    /['"](?.+)['"]/.match(message)[:path]

    end

    request.http_method do |message|

    case /\.(?[A-z]+)/.match(message)&.captures&.
    fi
    rst

    when "get" then "GET"

    else “Raise"

    end

    end

    additional_request_methods "Raise"

    custom_status_codes(

    600 => "SyntaxError...",

    )

    end

    6TFS
    1SPUPDPM
    fi
    MFSVCZMJLF
    /FYU EF
    fi
    OF3VCZMJLFQSPUPDPM
    JOUIF1SPUPDPM
    fi
    MF

    View Slide

  38. Toycol::Protocol.de
    fi
    ne(:rubylike) do

    request.path do |message|

    /['"](?.+)['"]/.match(message)[:path]

    end

    request.http_method do |message|

    case /\.(?[A-z]+)/.match(message)&.captures&.
    fi
    rst

    when "get" then "GET"

    else “Raise"

    end

    end

    additional_request_methods "Raise"

    custom_status_codes(

    600 => "SyntaxError...",

    )

    end

    6TFS
    1SPUPDPM
    fi
    MFSVCZMJLF
    1BTTUIFQSPUPDPMOBNFBTBOBSHVNFOU
    BOEBCMPDLGPSEF
    fi
    OJOHUIFQSPUPDPM
    UP5PZDPM1SPUPDPMEF
    fi
    OF
    ᶄ%F
    fi
    OFZPVSQSPUPDPMJOUIF1SPUPDPM
    fi
    MF

    View Slide

  39. Toycol::Protocol.de
    fi
    ne(:rubylike) do

    request.path do |message|

    /['"](?.+)['"]/.match(message)[:path]

    end

    request.http_method do |message|

    case /\.(?[A-z]+)/.match(message)&.captures&.
    fi
    rst

    when "get" then "GET"

    else “Raise"

    end

    end

    additional_request_methods "Raise"

    custom_status_codes(

    600 => "SyntaxError...",

    )

    end

    6TFS
    1SPUPDPM
    fi
    MFSVCZMJLF
    :PVOFFEUPEF
    fi
    OFIPXUPQBSTF
    UIFSFRVFTUNFTTBHFJOUIFTFCMPDLTPG
    [email protected]
    ᶄ%F
    fi
    OFZPVSQSPUPDPMJOUIF1SPUPDPM
    fi
    MF

    View Slide

  40. Toycol::Protocol.de
    fi
    ne(:rubylike) do

    request.path do |message|

    /['"](?.+)['"]/.match(message)[:path]

    end

    request.http_method do |message|

    case /\.(?[A-z]+)/.match(message)&.captures&.
    fi
    rst

    when "get" then "GET"

    else “Raise"

    end

    end

    additional_request_methods "Raise"

    custom_status_codes(

    600 => "SyntaxError...",

    )

    end

    6TFS
    1SPUPDPM
    fi
    MFSVCZMJLF
    #FTJEFT ZPVDBOEF
    fi
    OFZPVSPXOTUBUVTDPEFT
    [email protected]@NFUIPET
    BEEOFXSFRVFTUNFUIPETCZ
    [email protected]@DPEFTNFUIPE
    ᶄ%F
    fi
    OFZPVSQSPUPDPMJOUIF1SPUPDPM
    fi
    MF

    View Slide

  41. Toycol::Protocol.de
    fi
    ne(:rubylike) do

    request.path do |message|

    /['"](?.+)['"]/.match(message)[:path]

    end

    request.http_method do |message|

    case /\.(?[A-z]+)/.match(message)&.captures&.
    fi
    rst

    when "get" then "GET"

    else “Raise"

    end

    end

    additional_request_methods "Raise"

    custom_status_codes(

    600 => "SyntaxError...",

    )

    end

    6TFS
    1SPUPDPM
    fi
    MFSVCZMJLF
    *OUIJT3VCZMJLFQSPUPDPM
    *BEEFE3BJTFBTBOFXSFRVFTUNFUIPE
    BOEEF
    fi
    OFEBTBDVTUPNTUBUVTDPEF
    ᶄ%F
    fi
    OFZPVSQSPUPDPMJOUIF1SPUPDPM
    fi
    MF

    View Slide

  42. Toycol::Protocol.de
    fi
    ne(:rubylike) do

    request.path do |message|

    /['"](?.+)['"]/.match(message)[:path]

    end

    request.http_method do |message|

    case /\.(?[A-z]+)/.match(message)&.captures&.
    fi
    rst

    when "get" then "GET"

    else “Raise"

    end

    end

    additional_request_methods "Raise"

    custom_status_codes(

    600 => "SyntaxError...",

    )

    end

    6TFS
    1SPUPDPM
    fi
    MFSVCZMJLF
    ᶄ%F
    fi
    OFZPVSQSPUPDPMJOUIF1SPUPDPM
    fi
    MF
    *GUIFSFRVFTUNFTTBHFJTTVDDFTTGVMMZQBSTFE
    UIFSFRVFTUNFUIPEJT(&5
    PUIFSXJTFJUJT3BJTF

    View Slide

  43. ᶅ#VJMEB3BDLDPNQBUJCMFBQQMJDBUJPO
    require "rack"

    require "toycol"

    Toycol::Protocol.use(:rubylike)

    class App

    def call(env)

    case env["REQUEST_METHOD"]

    when “GET”

    [200, { "Content-Type" => "text/html" }, [“I love Ruby!”\n”, ”I love RubyKaigi!\n”]]

    when “Raise”

    [600, { "Content-Type" => "text/html" },

    [“Unexpected request message”]]

    end

    end

    end

    run App.new
    6TFS
    DPO
    fi
    [email protected]
    /FYU CVJMEB3BDLDPNQBUJCMFBQQMJDBUJPO
    UIBUSVOTPO3VCZMJLFQSPUPDPM

    View Slide

  44. require "rack"

    require "toycol"

    Toycol::Protocol.use(:rubylike)

    class App

    def call(env)

    case env["REQUEST_METHOD"]

    when “GET”

    [200, { "Content-Type" => "text/html" }, [“I love Ruby!”\n”, ”I love RubyKaigi!\n”]]

    when “Raise”

    [600, { "Content-Type" => "text/html" },

    [“Unexpected request message”]]

    end

    end

    end

    run App.new
    :PVOFFEUPSFRVJSFlUPZDPMz
    BOEDBMM5PZDPM1SPUPDPMVTFUPTQFDJGZ
    XIJDIQSPUPDPMZPVXPVMEMJLFUPVTF
    6TFS
    DPO
    fi
    [email protected]
    ᶅ#VJMEB3BDLDPNQBUJCMFBQQMJDBUJPO

    View Slide

  45. require "rack"

    require "toycol"

    Toycol::Protocol.use(:rubylike)

    class App

    def call(env)

    case env["REQUEST_METHOD"]

    when “GET”

    [200, { "Content-Type" => "text/html" }, [“I love Ruby!”\n”, ”I love RubyKaigi!\n”]]

    when “Raise”

    [600, { "Content-Type" => "text/html" },

    [“Unexpected request message”]]

    end

    end

    end

    run App.new
    *OUIJTBQQMJDBUJPO *EF
    fi
    OFEUPSFUVSO
    TUBUVTDPEFGPS(&5SFRVFTU
    BOETUBUVTDPEFGPS4ZOUBY&SSPSSFRVFTU
    6TFS
    DPO
    fi
    [email protected]
    ᶅ#VJMEB3BDLDPNQBUJCMFBQQMJDBUJPO

    View Slide

  46. ᶆ3VOUIFTFSWFS4FOEBSFRVFTUNFTTBHF
    5IFTFSWFSQSPHSBNUPSVOUIFQSPUPDPMBOEUIFDMJFOU
    QSPHSBNJOUPTFOEUIFSFRVFTUNFTTBHFCZUIFQSPUPDPM
    BSFCVJMUJOBTDPNNBOEMJOFUPPMT
    -FU`TTUBSUUIFTFSWFSTFOETPNFUIJOHUPSFRVFTU
    $ toycol client ??? (A request message by Rubylike protocol)
    $ toycol server con
    fi
    g_rubylike.ru
    4FSWFS$-*
    $MJFOU$-*

    View Slide

  47. -FUTTFOEBNFTTBHFUIBUTFFNTUPCF3VCZMJLFTZOUBY
    $ toycol client “‘/posts’.get”

    [Toycol] Sent request message: '/posts'.get

    ---

    [Toycol] Received response message:

    HTTP/1.1 200 OK

    Content-Type: text/html

    Content-Length: 31

    I love Ruby!

    I love RubyKaigi!
    5IJTJT3VCZMJLF JTO`UJU
    5IFBQQMJDBUJPOSFUVSOT0,
    ᶆ3VOUIFTFSWFS4FOEBSFRVFTUNFTTBHF

    View Slide

  48. -FU`TUSZBOBOPUIFSNFTTBHFUIBUNJHIUNJHIUSBJTFBFSSPS
    $ toycol client “get(‘/posts’)”

    [Toycol] Sent request message: get(‘/posts’)

    ---

    [Toycol] Received response message:

    HTTP/1.1 600 SyntaxError

    Content-Type: text/html

    Content-Length: 64

    Unexpected request message

    *TJUQBSTFBCMFCZ3VCZMJLFQSPUPDPMʜ
    5IFBQQMJDBUJPOSFUVSOT4ZOUBY&SSPS
    ᶆ3VOUIFTFSWFS4FOEBSFRVFTUNFTTBHF
    *U`TXPSLJOHDPSSFDUMZ

    View Slide

  49. *OUFSOBMJNQMFNFOUBUJPOPG5PZDPM

    View Slide

  50. /PXMFU`TFYQMPSFUIFJOTJEFPG5PZDPM
    GSPNUXPBTQFDUT
    5IFOFUXPSLDPNNVOJDBUJPO
    fl
    PX
    )PXUPUSBOTMBUFBSFRVFTUNFTTBHF

    View Slide

  51. 5IFOFUXPSLDPNNVOJDBUJPO
    fl
    PX

    View Slide

  52. 5IFOFUXPSLDPNNVOJDBUJPO
    fl
    PX
    -FUTUBLFBMPPLBUIPX5PZDPMXPSLTJOUFSOBMMZXJUIUIF
    OFUXPSLDPNNVOJDBUJPO
    fl
    PX)FSFJTBPWFSBMMWJFX
    $MJFOU
    1SPYZ4FSWFS
    #BDLHSPVOE4FSWFS "QQMJDBUJPO
    3BDL)BOEMFS
    1SPUPDPM%F
    fi
    OJUJPO

    View Slide

  53. 4UBSUTFSWFSTCZ3BDLIBOEMFS
    5PZDPMSVOT3BDLDPNQBUJCMFBQQMJDBUJPOT
    4PZPVTUBSUTFSWFSTGPS5PZDPMCZ3BDLIBOEMFS
    $MJFOU
    1SPYZ4FSWFS
    #BDLHSPVOE4FSWFS "QQMJDBUJPO
    3BDL)BOEMFS
    1SPUPDPM%F
    fi
    OJUJPO

    View Slide

  54. 4UBSUTFSWFSTXJUIUPZDPMTFSWFS
    UPZDPMTFSWFSDPNNBOEDBMMT3BDL4FSWFSTUBSUJOTJEF
    3BDL
    5IFO3BDL4FSWFSTUBSUDBMMT3BDL)BOEMFS5PZDPMSVO
    3BDL)BOEMFS5PZDPMSVO
    5PZDPM 3BDL
    UPZDPMTFSWFS 3BDL4FSWFSTUBSU

    View Slide

  55. 3BDL)BOEMFS5PZDPMSVO4UBSUTFSWFST
    module Rack

    module Handler

    class Toycol

    def self.run(app, _ = {})

    @app = app

    @host ||= ::Toycol::DEFAULT_HOST

    @port ||= "9292"

    if (child_pid = fork)

    ::Toycol::Proxy.new(@host, @port).start

    Process.waitpid(child_pid)

    else

    run_background_server

    end

    end

    end

    end

    end
    MJCSBDLIBOEMFSUPZDPMSC
    3BDL)BOEMFS5PZDPMSVO

    View Slide

  56. module Rack

    module Handler

    class Toycol

    def self.run(app, _ = {})

    @app = app

    @host ||= ::Toycol::DEFAULT_HOST

    @port ||= "9292"

    if (child_pid = fork)

    ::Toycol::Proxy.new(@host, @port).start

    Process.waitpid(child_pid)

    else

    run_background_server

    end

    end

    end

    end

    end
    4UBSUBQSPYZTFSWFS
    4UBSUBCBDLHSPVOETFSWFS
    MJCSBDLIBOEMFSUPZDPMSC
    'PSLUPTUBSUUXPLJOEPGTFSWFST
    BQSPYZTFSWFSBCBDLHSPVOETFSWFS

    JOUIJTNFUIPE
    3BDL)BOEMFS5PZDPMSVO4UBSUTFSWFST

    View Slide

  57. 5IFSPMFTPGUIFUXPTFSWFST
    5IFQSPYZTFSWFSJTGPSSFDFJWJOHSFRVFTUTUSBOTMBUJOH
    QSPUPDPM
    $MJFOU
    1SPYZ4FSWFS
    #BDLHSPVOE4FSWFS "QQMJDBUJPO
    3BDL)BOEMFS
    1SPUPDPM%F
    fi
    OJUJPO

    View Slide

  58. 5IFSPMFTPGUIFUXPTFSWFST
    5IFCBDLHSPVOETFSWFSJTGPSSVOOJOHUIFBQQMJDBUJPO
    $MJFOU
    1SPYZ4FSWFS
    #BDLHSPVOE4FSWFS "QQMJDBUJPO
    3BDL)BOEMFS
    1SPUPDPM%F
    fi
    OJUJPO

    View Slide

  59. 3BDL)[email protected]@TFSWFS
    def self.select_background_server

    case @prefferd_background_server

    when “puma”

    return “puma” if try_require_puma_handler

    raise LordError, “Puma is not installed in your environment.”

    when nil

    try_require_puma_handler ? “puma” : “builtin”

    else

    “builtin”

    rescue LordError

    Process.kill(:INT, Process.ppid)

    abort

    end
    MJCSBDLIBOEMFSUPZDPMSC
    "TBCBDLHSPVOETFSWFS VTF1VNBJGJUJTJOTUBMMFE
    JOZPVSFOWJSPONFOU0SVTFBTFSWFSUIBUJTCVJMUJOUP
    5PZDPMJUTFMGJGJUJTOPU

    View Slide

  60. 28IZTIPVMEXFVTFCPUI
    BQSPYZTFSWFSBOEBCBDLHSPVOETFSWFS

    View Slide

  61. "5PQFSGPSNNFTTBHFUSBOTMBUJPO
    BOEBQQMJDBUJPOFYFDVUJPOTFQBSBUFMZ
    4PNFBQQMJDBUJPOTCVJMUCZGSBNFXPSLTTVDIBT
    4JOBUSBPS3BJMTVTF1VNBCZEFGBVMU
    BOE*XPVMEO`UMJLFUPNPEJGZ1VNBPSUIFGSBNFXPSL
    UPNBLFUIFNXPSL

    View Slide

  62. 3FDFJWFBSFRVFTUNFTTBHFGSPNUIFDMJFOU
    4PGBS XFWFHPUUIFTFSWFSTVQBOESVOOJOH/FYU UIF
    QSPYZTFSWFSSFDFJWFTBSFRVFTUNFTTBHFGSPNUIFDMJFOU
    $MJFOU
    1SPYZ4FSWFS
    #BDLHSPVOE4FSWFS "QQMJDBUJPO
    3BDL)BOEMFS
    1SPUPDPM%F
    fi
    OJUJPO

    View Slide

  63. 5SBOTMBUFUIFSFRVFTUNFTTBHF
    "OEUIFOUIFQSPYZTFSWFSUSBOTMBUFTJUJOUPB)551
    GPSNBUUFESFRVFTUNFTTBHFVTJOHUIFQSPUPDPMEF
    fi
    OJUJPO
    $MJFOU
    1SPYZ4FSWFS
    #BDLHSPVOE4FSWFS "QQMJDBUJPO
    3BDL)BOEMFS
    1SPUPDPM%F
    fi
    OJUJPO

    View Slide

  64. def start



    loop do

    trap(:INT) { shutdown }

    @client = @proxy.accept

    while [email protected]? && [email protected]?

    begin

    request = @client.readpartial(CHUNK_SIZE)

    logger “Received message: #{request.inspect.chomp}”

    safe_execution! { @protocol.run!(request) }



    end

    end

    @client.close

    end

    end
    MJCUPZDPMQSPYZSC
    5PZDPM1SPYZTUBSU
    5PZDPM1SPYZTUBSU3FDFJWF5SBOTMBUF

    View Slide

  65. def start



    loop do

    trap(:INT) { shutdown }

    @client = @proxy.accept

    while [email protected]? && [email protected]?

    begin

    request = @client.readpartial(CHUNK_SIZE)

    logger “Received message: #{request.inspect.chomp}”

    safe_execution! { @protocol.run!(request) }



    end

    end

    @client.close

    end

    end
    !QSPYZ5$14FSWFSOFX !IPTU !QPSU

    MJCUPZDPMQSPYZSC
    3FDFJWFBSFRVFTUNFTTBHFGSPNUIFDMJFOU
    5PZDPM1SPYZTUBSU3FDFJWF5SBOTMBUF

    View Slide

  66. def start



    loop do

    trap(:INT) { shutdown }

    @client = @proxy.accept

    while [email protected]? && [email protected]?

    begin

    request = @client.readpartial(CHUNK_SIZE)

    logger “Received message: #{request.inspect.chomp}”

    safe_execution! { @protocol.run!(request) }



    end

    end

    @client.close

    end

    end
    !QSPUPDPM5PZDPM1SPUPDPM
    MJCUPZDPMQSPYZSC
    &YFDVUF5PZDPM1SPUPDPMSVOUPUSBOTMBUF
    UIFSFRVFTUNFTTBHFJOUP)551GPSNBU
    5PZDPM1SPYZTUBSU3FDFJWF5SBOTMBUF

    View Slide

  67. 4FOEBSFRVFTUNFTTBHFUPCBDLHSPVOETFSWFS
    5IFQSPYZTFSWFSTFOETB)551GPSNBUUFESFRVFTU
    NFTTBHFUPUIFCBDLHSPVOETFSWFS
    $MJFOU
    1SPYZ4FSWFS
    #BDLHSPVOE4FSWFS "QQMJDBUJPO
    3BDL)BOEMFS
    1SPUPDPM%F
    fi
    OJUJPO

    View Slide

  68. 3FDFJWFBSFTQPOTFNFTTBHFGSPNUIFCBDLHSPVOETFSWFS
    ʜBOESFDFJWFTUIFSFTVMUTPGUIFBQQMJDBUJPOFYFDVUFE
    GSPNUIFCBDLHSPVOETFSWFS
    $MJFOU
    1SPYZ4FSWFS
    #BDLHSPVOE4FSWFS "QQMJDBUJPO
    3BDL)BOEMFS
    1SPUPDPM%F
    fi
    OJUJPO

    View Slide

  69. [email protected]@TFSWFS
    def start



    loop do

    trap(:INT) { shutdown }

    @client = @proxy.accept

    while [email protected]? && [email protected]?

    begin



    http_request_message = build_http_request_message



    transfer_to_server(http_request_message)

    end

    end

    @client.close

    end

    end
    MJCUPZDPMQSPYZSC
    [email protected]@TFSWFS
    UIBUTFOETUIFSFRVFTUNFTTBHFUP
    UIFCBDLHSPVOETFSWFS

    View Slide

  70. [email protected]@TFSWFS3FRVFTU3FTQPOTF
    def transfer_to_server

    UNIXSocket.open(UNIIX_SOCKET_PATH) do |server|

    server.write request_message

    server.close_write



    response_message = []

    response_message << server.readpartial(CHUNK_SIZE) until server.eof?

    response_message = response_message.join



    end

    end
    MJCUPZDPMQSPYZSC
    [email protected]@TFSWFS

    View Slide

  71. [email protected]@TFSWFS3FRVFTU3FTQPOTF
    def transfer_to_server

    UNIXSocket.open(UNIIX_SOCKET_PATH) do |server|

    server.write request_message

    server.close_write



    response_message = []

    response_message << server.readpartial(CHUNK_SIZE) until server.eof?

    response_message = response_message.join



    end

    end
    MJCUPZDPMQSPYZSC
    $POOFDUUPUIFCBDLHSPVOETFSWFSXJUI
    B6/*9EPNBJOTPDLFU

    View Slide

  72. [email protected]@TFSWFS3FRVFTU3FTQPOTF
    def transfer_to_server

    UNIXSocket.open(UNIIX_SOCKET_PATH) do |server|

    server.write request_message

    server.close_write



    response_message = []

    response_message << server.readpartial(CHUNK_SIZE) until server.eof?

    response_message = response_message.join



    end

    end
    MJCUPZDPMQSPYZSC
    4FOEUIFSFRVFTUNFTTBHFUP
    UIFCBDLHSPVOETFSWFS
    5IFOUIFCBDLHSPVOETFSWFSSVOTUIFBQQMJDBUJPOʜ

    View Slide

  73. [email protected]@TFSWFS3FRVFTU3FTQPOTF
    def transfer_to_server

    UNIXSocket.open(UNIIX_SOCKET_PATH) do |server|

    server.write request_message

    server.close_write



    response_message = []

    response_message << server.readpartial(CHUNK_SIZE) until server.eof?

    response_message = response_message.join



    end

    end
    MJCUPZDPMQSPYZSC
    ʜPOUIFCBDLHSPVOE

    3FDFJWFUIFSFTQPOTFNFTTBHFGSPN
    UIFCBDLHSPVOETFSWFS

    View Slide

  74. 3FUVSOUIFSFTQPOTFNFTTBHFUPUIFDMJFOU
    5IFOUIFQSPYZTFSWFSSFUVSOTUIFSFTQPOTFNFTTBHF
    SFDFJWFEGSPNUIFCBDLHSPVOETFSWFSUPUIFDMJFOU
    $MJFOU
    1SPYZ4FSWFS
    #BDLHSPVOE4FSWFS "QQMJDBUJPO
    3BDL)BOEMFS
    1SPUPDPM%F
    fi
    OJUJPO

    View Slide

  75. [email protected]@TFSWFS3FUVSOUIFSFTQPOTF
    def transfer_to_server

    UNIXSocket.open(UNIIX_SOCKET_PATH) do |server|



    response_message = []

    response_message << server.readpartial(CHUNK_SIZE) until server.eof?

    response_message = response_message.join



    @client.write response_message

    @client.close_write

    logger "Finished to response to client"

    server.close

    end

    end
    MJCUPZDPMQSPYZSC
    3FUVSOTUIFSFTQPOTFNFTTBHFSFDFJWFE
    GSPNUIFCBDLHSPVOETFSWFSUPUIFDMJFOU

    View Slide

  76. )PXUPUSBOTMBUFBSFRVFTUNFTTBHF

    View Slide

  77. 5IJTJTIPX5PZDPMXPSLTJOUFSOBMMZGSPNUIFEF
    fi
    OJOHPG
    UIFQSPUPDPMVOUJMUSBOTMBUJOHBSFRVFTUNFTTBHFXJUIJU
    DMBTT1SPUPDPM
    $MJFOU
    1SPUPDPM%F
    fi
    OJUJPO
    1SPYZ4FSWFS
    UPZDPMTFSWFS "QQMJDBUJPO
    )PXUPUSBOTMBUFBSFRVFTUNFTTBHF

    View Slide

  78. "HBJO UIFTUBSUJOHQPJOUGPSUIFQSPHSBNFYFDVUJPOJT
    UPZDPMTFSWFSDPNNBOE
    DMBTT1SPUPDPM
    $MJFOU 1SPYZ4FSWFS
    "QQMJDBUJPO
    UPZDPMTFSWFS
    1SPUPDPM%F
    fi
    OJUJPO
    4UBSUUIFQSPHSBNXJUIUPZDPMTFSWFS

    View Slide

  79. 4UBSUUIFQSPHSBNXJUIUPZDPMTFSWFS
    "DUVBMMZ 3BDL4FSWFSTUBSUDBMMT3BDL#[email protected]
    fi
    MF
    BOE3BDL#[email protected]@TUSJOHUPMPBEUIFBQQMJDBUJPO
    fi
    MFCFGPSFDBMMJOH3BDL)BOEMFS5PZDPMSVO
    3BDL)BOEMFS5PZDPMSVO
    5PZDPM 3BDL
    UPZDPMTFSWFS 3BDL4FSWFSTUBSU
    3BDL#[email protected]
    fi
    MF
    3BDL#[email protected]@TUSJOH

    View Slide

  80. 4P UIFQSPHSBNXJMMMPBEUIFBQQMJDBUJPO
    fi
    MFCFGPSF
    TUBSUJOHUIFTFSWFS
    $MJFOU 1SPYZ4FSWFS
    "QQMJDBUJPO
    UPZDPMTFSWFS
    -PBEUIFBQQMJDBUJPO
    fi
    MF
    DMBTT1SPUPDPM
    1SPUPDPM%F
    fi
    OJUJPO

    View Slide

  81. 5IFBQQMJDBUJPO3FRVJSFlUPZDPMz
    require "rack"

    require "toycol"

    Toycol::Protocol.use(:rubylike)

    class App

    def call(env)

    case env["REQUEST_METHOD"]

    when “GET”

    [200, { "Content-Type" => "text/html" }, [“I love Ruby!”\n”, ”I love RubyKaigi!\n”]]

    when “OTHER”

    [600, { "Content-Type" => "text/html" },

    [“Sorry, but I'd like you to speak more like a Ruby programmer…”]]

    end

    end

    end

    run App.new
    6TFS
    DPO
    fi
    [email protected]
    *OUIFBQQMJDBUJPO
    fi
    MF
    UPZDPMJTSFRVJSFEUPSVO5PZDPM

    View Slide

  82. NPEVMF5PZDPM-PBE1SPUPDPM
    fi
    MFT
    require "
    fi
    leutiils"

    require “optparse"

    require “rack"

    require “scoket”

    require “stringio”



    module Toycol



    end

    Dir["#{FileUtils.pwd}/Protocol
    fi
    le*"].sort.each { |f| load f }
    MJCUPZDPMSC
    8IFO5PZDPMJTSFRVJSFE
    UIJT
    fi
    MFOBNFEUPZDPMSCJOUIF5PZDPMHFN
    XJMMCFMPBEFE

    View Slide

  83. require "
    fi
    leutiils"

    require “optparse"

    require “rack"

    require “scoket”

    require “stringio”



    module Toycol



    end

    Dir["#{FileUtils.pwd}/Protocol
    fi
    le*"].sort.each { |f| load f }
    5PZDPMMPBET1SPUPDPM
    fi
    MFTUIBUBSFQMBDFE
    JOUIFTBNFEJSFDUPSZBTUIFBQQMJDBUJPO
    fi
    MF
    NPEVMF5PZDPM-PBE1SPUPDPM
    fi
    MFT
    1SPUPDPM
    fi
    MF"DPO
    fi
    HVSBUJPO
    fi
    MFDPOUBJOJOHQSPUPDPMEF
    fi
    OJUJPOT
    MJCUPZDPMSC

    View Slide

  84. 4P UIF
    fi
    STUDPEFTUPCFMPBEFEJTUIFQSPUPDPMEF
    fi
    OJUJPO
    XIFOUIFQSPHSBNJTTUBSUFE
    $MJFOU 1SPYZ4FSWFS
    "QQMJDBUJPO
    UPZDPMTFSWFS
    -PBE1SPUPDPM
    fi
    MFT
    DMBTT1SPUPDPM
    1SPUPDPM%F
    fi
    OJUJPO

    View Slide

  85. 1SPUPDPM
    fi
    MF4UPSFUIFFOUJSFEF
    fi
    OJUJPO
    Toycol::Protocol.de
    fi
    ne(:rubylike) do

    request.path do |message|

    /['"](?.+)['"]/.match(message)[:path]

    end

    request.http_method do |message|

    case /\.(?[A-z]+)/.match(message)&.captures&.
    fi
    rst

    when "get" then "GET"

    else "OTHER"

    end

    end

    additional_request_methods "OTHER"

    custom_status_codes(

    600 => "Hmm...",

    )

    end

    6TFS
    1SPUPDPM
    fi
    MFSVCZMJLF
    'PSFYBNQMF1SPUPDPM
    fi
    MFPG3VCZMJLFQSPUPDPM

    View Slide

  86. 1SPUPDPM
    fi
    MF4UPSFUIFFOUJSFEF
    fi
    OJUJPO
    Toycol::Protocol.de
    fi
    ne(:rubylike) do

    request.path do |message|

    /['"](?.+)['"]/.match(message)[:path]

    end

    request.http_method do |message|

    case /\.(?[A-z]+)/.match(message)&.captures&.
    fi
    rst

    when "get" then "GET"

    else "OTHER"

    end

    end

    additional_request_methods "OTHER"

    custom_status_codes(

    600 => "Hmm...",

    )

    end

    6TFS
    1SPUPDPM
    fi
    MFSVCZMJLF
    5IFDPEFTJOUIFCMPDLPG
    5PZDPM1SPUPDPMEF
    fi
    OFBSFTUPSFE
    JO5PZDPM1SPUPDPMDMBTT

    View Slide

  87. 5PZDPM1SPUPDPMEF
    fi
    OF4UPSFUIFEF
    fi
    OJUJPO
    NPEVMF5PZDPM
    DMBTT1SPUPDPM
    !EF
    fi
    OFNFOUT\^
    ʜ
    EFGTFMGEF
    fi
    OF [email protected] CMPDL

    ʜ
    !EF
    fi
    OFNFOUTCMPDL
    FOE
    ʜ
    FOE
    FOE
    MJCUPZDPMQSPUPDPMSC
    5PZDPM1SPUPDPMEF
    fi
    OF

    View Slide

  88. 5PZDPM1SPUPDPMEF
    fi
    OF4UPSFUIFEF
    fi
    OJUJPO
    NPEVMF5PZDPM
    DMBTT1SPUPDPM
    !EF
    fi
    OFNFOUT\^
    ʜ
    EFGTFMGEF
    fi
    OF [email protected] CMPDL

    ʜ
    !EF
    fi
    OFNFOUTCMPDL
    FOE
    ʜ
    FOE
    FOE
    MJCUPZDPMQSPUPDPMSC
    "TTJHOBIBTIPCKFDUUPBJOTUBODFWBSJBCMF!EF
    fi
    OFNFOUT
    4UPSFUIFQSPUPDPMOBNFBTUIFLFZBOEUIFCMPDLEF
    fi
    OJOH
    UIFQSPUPDPMBTUIFWBMVFJOUIJTIBTIPCKFDU

    View Slide

  89. 1SPUPDPM
    fi
    MF4UPSFBEFUBJMFEEF
    fi
    OJUJPOGPSFBDI
    Toycol::Protocol.de
    fi
    ne(:rubylike) do

    request.path do |message|

    /['"](?.+)['"]/.match(message)[:path]

    end

    request.http_method do |message|

    case /\.(?[A-z]+)/.match(message)&.captures&.
    fi
    rst

    when "get" then "GET"

    else "OTHER"

    end

    end

    additional_request_methods "OTHER"

    custom_status_codes(

    600 => "Hmm...",

    )

    end

    6TFS
    1SPUPDPM
    fi
    MFSVCZMJLF
    &BDIMPHJDTEF
    fi
    OFEJOUIFTFCMPDLTJT
    TUPSFEJOFBDIWBSJBCMFTPG
    5PZDPM1SPUPDPMDMBTT

    View Slide

  90. NPEVMF5PZDPM
    DMBTT1SPUPDPM
    ʜ
    EFGTFMGSFRVFTU
    !SFRVFTUcc$MBTTOFXEP
    EFGTFMGQBUI CMPDL

    !QBUICMPDL
    FOE
    [email protected] CMPDL

    [email protected]
    FOE
    ʜ
    FOE
    FOE
    ʜ
    FOE
    FOE
    FH5PZDPM1SPUPDPMSFRVFTU4UPSFUIFEF
    fi
    OJUJPO
    MJCUPZDPMQSPUPDPMSC
    'PSFYBNQMF5PZDPM1SPUPDPMSFRVFTU

    View Slide

  91. NPEVMF5PZDPM
    DMBTT1SPUPDPM
    ʜ
    EFGTFMGSFRVFTU
    !SFRVFTUcc$MBTTOFXEP
    EFGTFMGQBUI CMPDL

    !QBUICMPDL
    FOE
    [email protected] CMPDL

    [email protected]
    FOE
    ʜ
    FOE
    FOE
    ʜ
    FOE
    FOE
    FH5PZDPM1SPUPDPMSFRVFTU4UPSFUIFEF
    fi
    OJUJPO
    MJCUPZDPMQSPUPDPMSC
    5IJTJTGPSBTTJHOJOHBOBOPOZNPVTDMBTTUP
    !SFRVFTUPG5PZDPM1SPUPDPM
    5IJTBOPOZNPVTDMBTTJTVTFEUPTUPSFFBDIMPHJDGPS
    QBSTJOHUIFSFRVFTUNFTTBHFJOJUTPXOJOTUBODFWBSJBCMF

    View Slide

  92. FH5PZDPM1SPUPDPMSFRVFTU4UPSFUIFEF
    fi
    OJUJPO
    NPEVMF5PZDPM
    DMBTT1SPUPDPM
    ʜ
    EFGTFMGSFRVFTU
    !SFRVFTUcc$MBTTOFXEP
    EFGTFMGQBUI CMPDL

    !QBUICMPDL
    FOE
    [email protected] CMPDL

    [email protected]
    FOE
    ʜ
    FOE
    FOE
    ʜ
    FOE
    FOE
    MJCUPZDPMQSPUPDPMSC
    &BDIJOTUBODFWBSJBCMFPGUIJTBOPOZNPVT
    DMBTTTUPSFTBCMPDLPGMPHJDUPHFU
    UIFSFRVFTUQBUI )551NFUIPE FUD
    SFRVFTUQBUIEPcNFTTBHFc
    /['"](?.+)['"]/.match(message)[:path]

    end

    request.http_method do |message|

    case /\.(?[A-z]+)/.match(message)&.captures&.
    fi
    rst

    when "get" then "GET"

    else "OTHER"

    end

    end
    6TFS
    1SPUPDPM
    fi
    MFSVCZMJLF

    View Slide

  93. FH5PZDPM1SPUPDPMSFRVFTU4UPSFUIFEF
    fi
    OJUJPO
    NPEVMF5PZDPM
    DMBTT1SPUPDPM
    ʜ
    EFGTFMGSFRVFTU
    !SFRVFTUcc$MBTTOFXEP
    EFGTFMGQBUI CMPDL

    !QBUICMPDL
    FOE
    [email protected] CMPDL

    [email protected]
    FOE
    ʜ
    FOE
    FOE
    ʜ
    FOE
    FOE
    MJCUPZDPMQSPUPDPMSC
    :PVDBOSFUSJFWFUIFTUPSFEMPHJDTCZFYFDVUJOHDBMMNFUIPE
    POFBDICMPDLTUPSFEJOUIFTFWBSJBCMFT
    5PZDPM1SPUPDPMDMBTTIBTTPNFJOUFSGBDFTUIBU
    BMMPXTUIFTFUPCFDBMMFEBUBOZUJNF

    View Slide

  94. View Slide

  95. [email protected]
    fi
    OJUJPO
    NPEVMF5PZDPM
    DMBTT1SPUPDPM
    ʜ
    [email protected]
    [email protected]@[email protected] l!QBUIz
    DBMM [email protected]

    ʜ
    [email protected]
    FOE
    FOE
    FOE
    MJCUPZDPMQSPUPDPMSC
    5IJTJTUIFNFUIPEUPHFUUIFSFRVFTUQBUI
    GSPNBSFRVFTUNFTTBHFBOE
    BMPHJDUPQBSTFUIFSFRVFTUNFTTBHF

    View Slide

  96. [email protected]
    fi
    OJUJPO
    NPEVMF5PZDPM
    DMBTT1SPUPDPM
    ʜ
    [email protected]
    [email protected]@[email protected] l!QBUIz
    DBMM [email protected]

    ʜ
    [email protected]
    FOE
    FOE
    FOE
    MJCUPZDPMQSPUPDPMSC
    [email protected]@BUUSJCVUFT
    ʜ
    [email protected]
    ʜ
    FOE
    MJCUPZDPMQSPYZSC
    5IFQSPYZTFSWFSVTFTUIJTNFUIPEUPSFUSJFWF
    UIFSFRVFTUQBUI XIFOUIFQSPYZTFSWFSCVJMET
    B)551GPSNBUUFESFRVFTUNFTTBHF

    View Slide

  97. "GUFSUIF1SPUPDPM
    fi
    MFIBTCFFOMPBEFE UIFBQQMJDBUJPO
    TFMFDUTXIJDIQSPUPDPMUPVTF
    $MJFOU 1SPYZ4FSWFS
    "QQMJDBUJPO
    UPZDPMTFSWFS
    4FMFDUXIJDIQSPUPDPMUPVTF
    DMBTT1SPUPDPM
    1SPUPDPM%F
    fi
    OJUJPO

    View Slide

  98. "QQMJDBUJPO4FMFDUXIJDIQSPUPDPMUPVTF
    require "rack"

    require "toycol"

    Toycol::Protocol.use(:rubylike)

    class App

    def call(env)

    case env["REQUEST_METHOD"]

    when “GET”

    [200, { "Content-Type" => "text/html" }, [“I love Ruby!”\n”, ”I love RubyKaigi!\n”]]

    when “OTHER”

    [600, { "Content-Type" => "text/html" },

    [“Sorry, but I'd like you to speak more like a Ruby programmer…”]]

    end

    end

    end

    run App.new
    DPO
    fi
    [email protected]
    1BTTUIFQSPUPDPMOBNFZPVXBOUUPVTFUP
    5PZDPM1SPUPDPMVTF

    View Slide

  99. NPEVMF5PZDPM
    DMBTT1SPUPDPM
    ʜ
    EFGTFMGVTF [email protected]

    [email protected]@OBNF
    FOE
    ʜ
    FOE
    FOE
    5PZDPM1SPUPDPMVTF4UPSFUIFQSPUPDPMOBNF
    MJCUPZDPMQSPUPDPMSC
    5PZDPM1SPUPDPMVTF

    View Slide

  100. 5SBOTMBUFBNFTTBHFXJUIQSPUPDPMUPVTF
    'JOBMMZ USBOTMBUFUIFSFRVFTUNFTTBHFVTJOHUIFQSPUPDPM
    ZPVEF
    fi
    OFE
    $MJFOU 1SPYZ4FSWFS
    "QQMJDBUJPO
    UPZDPMTFSWFS DMBTT1SPUPDPM
    1SPUPDPM%F
    fi
    OJUJPO

    View Slide

  101. def start



    loop do

    trap(:INT) { shutdown }

    @client = @proxy.accept

    while [email protected]? && [email protected]?

    begin



    safe_execution! { @protocol.run!(request) }

    assign_parsed_attributes!

    http_request_message = build_http_request_message



    end

    end

    @client.close

    end

    end
    MJCUPZDPMQSPYZSC
    5PZDPM1SPYZTUBSU"QQMZUIFQSPUPDPM
    5PZDPM1SPYZTUBSUFYFDVUFT
    UIFQSPUPDPMUSBOTMBUJPO

    View Slide

  102. def start



    loop do

    trap(:INT) { shutdown }

    @client = @proxy.accept

    while [email protected]? && [email protected]?

    begin



    safe_execution! { @protocol.run!(request) }

    assign_parsed_attributes!

    http_request_message = build_http_request_message



    end

    end

    @client.close

    end

    end
    !QSPUPDPM5PZDPM1SPUPDPM
    MJCUPZDPMQSPYZSC
    5PZDPM1SPYZTUBSU"QQMZUIFQSPUPDPM
    &YFDVUF5PZDPM1SPUPDPMSVO
    UIFOUIFQSPUPDPMXJMMCFBQQMJFE
    UPUIFSFRVFTUNFTTBHF

    View Slide

  103. NPEVMF5PZDPM
    DMBTT1SPUPDPM
    ʜ
    EFGTFMGSVO NFTTBHF

    [email protected]
    SFUVSOVOMFTT CMPDL!EF
    fi
    OFNFOUT

    [email protected] [email protected] CMPDL

    FOE
    ʜ
    FOE
    FOE
    5PZDPM1SPUPDPMSVO"QQMZUIFQSPUPDPM
    MJCUPZDPMQSPUPDPMSC
    5PZDPM1SPUPDPMSVO

    View Slide

  104. NPEVMF5PZDPM
    DMBTT1SPUPDPM
    ʜ
    EFGTFMGSVO NFTTBHF

    [email protected]
    SFUVSOVOMFTT CMPDL!EF
    fi
    OFNFOUT

    [email protected] [email protected] CMPDL

    FOE
    ʜ
    FOE
    FOE
    5PZDPM1SPYZTUBSU"QQMZUIFQSPUPDPM
    MJCUPZDPMQSPUPDPMSC
    'JOEUIFQSPUPDPMEF
    fi
    OJUJPO
    [email protected]
    JO!EF
    fi
    OFNFOUTIBTI

    View Slide

  105. NPEVMF5PZDPM
    DMBTT1SPUPDPM
    ʜ
    EFGTFMGSVO NFTTBHF

    [email protected]
    SFUVSOVOMFTT CMPDL!EF
    fi
    OFNFOUT

    [email protected] [email protected] CMPDL

    FOE
    ʜ
    FOE
    FOE
    5PZDPM1SPYZTUBSU"QQMZUIFQSPUPDPM
    MJCUPZDPMQSPUPDPMSC
    &[email protected]
    5IFSFRVFTUNFTTBHFQBTTFEBT
    UIFBSHVNFOUXJMMCFUSBOTMBUFE

    View Slide

  106. def start



    loop do

    trap(:INT) { shutdown }

    @client = @proxy.accept

    while [email protected]? && [email protected]?

    begin



    safe_execution! { @protocol.run!(request) }

    assign_parsed_attributes!

    http_request_message = build_http_request_message



    end

    end

    @client.close

    end

    end
    MJCUPZDPMQSPYZSC
    5PZDPM1SPYZTUBSU#VJMEBSFRVFTUNFTTBHF
    [email protected]@BUUSJCVUFT
    [email protected]@[email protected]
    CVJMEB)551GPSNBUUFESFRVFTUNFTTBHF

    View Slide

  107. NPEVMF5PZDPM
    DMBTT1SPUPDPM


    def assign_parsed_attibutes

    @request_method = @protocol.request_method

    @path = @protocol.request_path

    @query = @protocol.query

    @input = @protocol.input

    end



    end

    end
    MJCUPZDPMQSPYZSC
    [email protected]@BUUSJCVUFT#VJMEBSFRVFTUNFTTBHF
    [email protected]@BUUSJCVUFTFYUSBDUT
    WBMVFTTUPSFEJO5PZDPM1SPUPDPMDMBTT
    "OEBTTJHOTUIFNUPFBDIJOTUBODFWBSJBCMFT

    View Slide

  108. NPEVMF5PZDPM
    DMBTT1SPUPDPM


    def build_http_request_message

    request_message = "#{request_line}#{request_header}\r\n"

    request_message += @input if @input

    request_message

    end



    end

    end
    MJCUPZDPMQSPYZSC
    [email protected]@[email protected]#VJMEBSFRVFTUNFTTBHF
    [email protected]@[email protected]
    B)551GPSNBUUFESFRVFTUNFTTBHF
    VTJOHUIPTFJOTUBODFWBSJBCMFT

    View Slide

  109. NPEVMF5PZDPM
    DMBTT1SPUPDPM


    def build_http_request_message

    request_message = "#{request_line}#{request_header}\r\n"

    request_message += @input if @input

    request_message

    end



    end

    end
    MJCUPZDPMQSPYZSC
    [email protected]@[email protected]#VJMEBSFRVFTUNFTTBHF
    FH
    [email protected]@[email protected]
    BWBMVFMJLF
    (&5QPTUT)551aSaO$POUFOU-FOHUIaSaOaSaO
    "#{@request_method} #{request_path} HTTP/1.1\r\n"
    “#{@path}#{"?#{@query}" if @query && [email protected]?}"

    "Content-Length: #{@input&.bytesize || 0}\r\n"

    View Slide

  110. "EWBODFEQSPUPDPMEF
    fi
    OJUJPOT

    View Slide

  111. -FUTDPOTJEFSTPNFNPSF
    BEWBODFEEF
    fi
    OJUJPOTPGQSPUPDPM

    View Slide

  112. -FU`TDPOTJEFSBHBJO3VCZMJLFQSPUPDPM
    XFKVTUEF
    fi
    OFE
    5IJTQSPUPDPMUBLFTB3VCZMJLFTZOUBY
    SFRVFTUNFTTBHFBTBTUSJOH
    QBSTFTJU BOEUSBOTMBUFTJUJOUP
    B)551GPSNBUUFESFRVFTUNFTTBHF

    View Slide

  113. 'PSFYBNQMF
    JGZPVSFDFJWFBTUSJOH
    lbQPTUT`HFUz
    BTBSFRVFTUNFTTBHF
    XJUIUIF3VCZMJLFQSPUPDPMʜ

    View Slide

  114. 3VCZMJLFQSPUPDPM1BSTJOHUIFTUSJOHlbQPTUT`HFUz
    Toycol::Protocol.de
    fi
    ne(:rubylike) do

    request.path do |message|

    /['"](?.+)['"]/.match(message)[:path]

    end

    request.http_method do |message|

    case /\.(?[A-z]+)/.match(message)&.captures&.
    fi
    rst

    when "get" then "GET"

    else "OTHER"

    end

    end



    end
    6TFS
    1SPUPDPM
    fi
    MFSVCZMJLF
    1BSTFUIFTUSJOHlbQPTUTHFUzUPHFUBSFRVFTUQBUI
    BOESFRVFTUNFUIPE TUPSFUIFNJO5PZDPM1SPUPDPM
    DMBTT BOEUSBOTMBUFUIFNUP)551GPSNBU
    lQPTUTzBTBSFRVFTUQBUI
    l(&5zBTBSFRVFTUNFUIPE

    View Slide

  115. 8IBUBCPVUBQSPUPDPMUIBUDBOSFDFJWF
    BOEFYFDVUFUIFSFRVFTUNFTTBHF
    BTBOBDUVBM3VCZTDSJQU
    SBUIFSUIBOBTBTUSJOH

    View Slide

  116. 'PSFYBNQMF
    JGZPVSFDFJWFB3VCZTDSJQU
    bQPTUT`HFU
    UIBUDBMMTBNFUIPEHFU
    POB4USJOHPCKFDUbQPTUT`
    BTBSFRVFTUNFTTBHFʜ

    View Slide

  117. Toycol::Protocol.de
    fi
    ne(:ruby) do |message|

    using Module.new {

    re
    fi
    ne String do

    def get

    Toycol::Protocol.request.path { self }

    Toycol::Protocol.request.http_method { "GET" }

    end

    end

    }

    instance_eval message

    end
    3VCZQSPUPDPM
    6TFS
    1SPUPDPM
    fi
    MFSVCZ
    5IJTJTBEF
    fi
    OJUJPOPG3VCZQSPUPDPM
    UIBUJTCFBCMFUPFYFDVUF
    BSFRVFTUNFTTBHFBTB3VCZTDSJQU
    3VCZQSPUPDPM

    View Slide

  118. 3VCZQSPUPDPM
    Toycol::Protocol.de
    fi
    ne(:ruby) do |message|

    using Module.new {

    re
    fi
    ne String do

    def get

    Toycol::Protocol.request.path { self }

    Toycol::Protocol.request.http_method { "GET" }

    end

    end

    }

    instance_eval message

    end
    6TFS
    1SPUPDPM
    fi
    MFSVCZ
    "EEHFUNFUIPEUP4USJOHDMBTTUPCFBCMF
    UPDBMMJUGPSlQPTUTzBTB4USJOHPCKFDU

    View Slide

  119. Toycol::Protocol.de
    fi
    ne(:ruby) do |message|

    using Module.new {

    re
    fi
    ne String do

    def get

    Toycol::Protocol.request.path { self }

    Toycol::Protocol.request.http_method { "GET" }

    end

    end

    }

    instance_eval message

    end
    3VCZQSPUPDPM
    6TFS
    1SPUPDPM
    fi
    MFSVCZ
    [email protected]
    UIFSFDFJWFESFRVFTUNFTTBHF lQPTUTzHFU

    BTB3VCZTDSJQU
    lQPTUTzHFU

    View Slide

  120. )PXFWFS BTZPVDBOTFF UIJTMPHJD
    DPOUBJOTBOJOKFDUJPOWVMOFSBCJMJUZ
    5IFSFJTOPXBZUPQSFWFOU
    UIFFYFDVUJPOPGBEBOHFSPVTTDSJQU
    XIFOJUJTSFDFJWFE
    TVDIBTTZTUFN lSNSGz

    View Slide

  121. 6OGPSUVOBUFMZ XFEPOULOPX
    XIBULJOEPGNFTTBHFT
    UIFDMJFOUXJMMTFOEUPUIFTFSWFS
    5IFSFGPSF 5PZDPMEPFTOPUBMMPXUP
    XSJUFDPEFTDPOUBJOJOHTVDI
    WVMOFSBCJMJUJFTJO1SPUPDPM
    fi
    MF

    View Slide

  122. def start



    while [email protected]? && [email protected]?

    begin

    request = @client.readpartial(CHUNK_SIZE)

    logger “Received message: #{request.inspect.chomp}”

    safe_execution! { @protocol.run!(request) }



    end

    end



    end
    MJCUPZDPMQSPYZSC
    4BGFFYFDVUJPOPGUIFQSPUPDPM
    5PZDPM1SPUPDPMSVOJTFYFDVUFEJOTJEF
    [email protected]
    OPWVMOFSBCMFEF
    fi
    OJUJPOTBSFJO1SPUPDPM
    fi
    MFT

    View Slide

  123. def safe_execution!(&block)

    safe_executionable_tp.enable(&block)

    end

    def safe_executionable_tp

    @safe_executionable_tp ||= TracePoint.new(:script_compiled) do |tp|

    if tp.binding.receiver == Protocol \

    && tp.method_id.to_s.match? /(.*eval|.*exec|`.+|%x\(|system|open|require|load)/

    raise UnauthorizeError, …

    end

    end

    end
    5PZDPM)[email protected]
    MJCUPZDPMIFMQFSSC
    5PZDPM)[email protected]
    "OE5PZDPM)[email protected]@UQ
    [email protected]

    View Slide

  124. def safe_execution!(&block)

    safe_executionable_tp.enable(&block)

    end

    def safe_executionable_tp

    @safe_executionable_tp ||= TracePoint.new(:script_compiled) do |tp|

    if tp.binding.receiver == Protocol \

    && tp.method_id.to_s.match? /(.*eval|.*exec|`.+|%x\(|system|open|require|load)/

    raise UnauthorizeError, …

    end

    end

    end
    5PZDPM)[email protected]
    MJCUPZDPMIFMQFSSC
    6TF5SBDF1PJOUUPEFUFDUJGBOZEBOHFSPVT
    NFUIPETBSFCFJOHFYFDVUFEUIBUDPVME
    DBVTFJOKFDUJPOWVMOFSBCJMJUJFTɹ

    View Slide

  125. def safe_execution!(&block)

    safe_executionable_tp.enable(&block)

    end

    def safe_executionable_tp

    @safe_executionable_tp ||= TracePoint.new(:script_compiled) do |tp|

    if tp.binding.receiver == Protocol \

    && tp.method_id.to_s.match? /(.*eval|.*exec|`.+|%x\(|system|open|require|load)/

    raise UnauthorizeError, …

    end

    end

    end
    5PZDPM)[email protected]
    MJCUPZDPMIFMQFSSC
    5BSHFUNFUIPETBSFJODMVEJOHFWBMPSFYFD
    JOUIBUOBNF FYFDVUJOHFYUFSOBMDPNNBOET
    BOEMPBEJOH
    fi
    MFTGSPNPVUTJEF

    View Slide

  126. 4P XFOFFEUPDPOTJEFSPG
    BNPSFTFDVSFXBZUPEF
    fi
    OFBQSPUPDPM
    UPSVOUIFBQQMJDBUJPO
    XJUI3VCZTDSJQUTBTSFRVFTUNFTTBHFT

    View Slide

  127. )PXBCPVUUIJT

    View Slide

  128. 'PSFYBNQMF BTCFGPSF
    JGZPVSFDFJWFB3VCZTDSJQU
    bQPTUT`HFU
    UIBUDBMMTBNFUIPEHFU
    POB4USJOHPCKFDUbQPTUT`
    BTBSFRVFTUNFTTBHFʜ

    View Slide

  129. 4BGF3VCZQSPUPDPM
    module SafeRuby

    PARSER_REGEX = /["'](?\/.*)["']\.(?[A-z]+)/

    end

    Toycol::Protocol.de
    fi
    ne(:safe_ruby) do |message|

    using Module.new {

    re
    fi
    ne String do

    def get

    Toycol::Protocol.request.http_method { “GET" }

    end

    end

    }

    path, method = SafeRuby::PARSER_REGEX.match(message) { |m| [m[:path], m[:method]] }

    request.path { path }

    request_path.public_send(method)

    end
    6TFS
    1SPUPDPM
    fi
    [email protected]
    4BGF3VCZQSPUPDPM

    View Slide

  130. module SafeRuby

    PARSER_REGEX = /["'](?\/.*)["']\.(?[A-z]+)/

    end

    Toycol::Protocol.de
    fi
    ne(:safe_ruby) do |message|

    using Module.new {

    re
    fi
    ne String do

    def get

    Toycol::Protocol.request.http_method { “GET" }

    end

    end

    }

    path, method = SafeRuby::PARSER_REGEX.match(message) { |m| [m[:path], m[:method]] }

    request.path { path }

    request_path.public_send(method)

    end
    4BGF3VCZQSPUPDPM"EEBQBSTFS3FHFY
    6TFS
    1SPUPDPM
    fi
    [email protected]
    1SFQBSFBSFHVMBSFYQSFTTJPOUPQBSTF
    UIFSFRVFTUNFTTBHF

    View Slide

  131. module SafeRuby

    PARSER_REGEX = /["'](?\/.*)["']\.(?[A-z]+)/

    end

    Toycol::Protocol.de
    fi
    ne(:safe_ruby) do |message|

    using Module.new {

    re
    fi
    ne String do

    def get

    Toycol::Protocol.request.http_method { “GET" }

    end

    end

    }

    path, method = SafeRuby::PARSER_REGEX.match(message) { |m| [m[:path], m[:method]] }

    request.path { path }

    request_path.public_send(method)

    end
    6TJOHUIJTSFHVMBSFYQSFTTJPO HFUUXPTUSJOHT
    ᶃQBUIlQPTUTz "SFRVFTUQBUI

    ᶄNFUIPElHFUz "NFUIPEOBNFUPCFDBMMFEGPSUIFSFRVFTUQBUI

    4BGF3VCZQSPUPDPM(FUBSFRVFTUQBUINFUIPE
    6TFS
    1SPUPDPM
    fi
    [email protected]
    ᶃ ᶄ

    View Slide

  132. module SafeRuby

    PARSER_REGEX = /["'](?\/.*)["']\.(?[A-z]+)/

    end

    Toycol::Protocol.de
    fi
    ne(:safe_ruby) do |message|

    using Module.new {

    re
    fi
    ne String do

    def get

    Toycol::Protocol.request.http_method { “GET" }

    end

    end

    }

    path, method = SafeRuby::PARSER_REGEX.match(message) { |m| [m[:path], m[:method]] }

    request.path { path }

    request_path.public_send(method)

    end
    1SPUPDPM
    fi
    [email protected]
    4BGF3VCZQSPUPDPM4UPSFUIFSFRVFTUQBUI
    4UPSFlQPTUTzBTBSFRVFTUQBUI
    JO5PZDPM1SPUPDPMSFRVFTUQBUI

    View Slide

  133. module SafeRuby

    PARSER_REGEX = /["'](?\/.*)["']\.(?[A-z]+)/

    end

    Toycol::Protocol.de
    fi
    ne(:safe_ruby) do |message|

    using Module.new {

    re
    fi
    ne String do

    def get

    Toycol::Protocol.request.http_method { “GET" }

    end

    end

    }

    path, method = SafeRuby::PARSER_REGEX.match(message) { |m| [m[:path], m[:method]] }

    request.path { path }

    request_path.public_send(method)

    end
    1SPUPDPM
    fi
    [email protected]
    4BGF3VCZQSPUPDPM3FUSJFWFUIFSFRVFTUQBUI
    5IFO [email protected]
    SFUVSOTB4USJOHPCKFDUlQPTUTz
    KVTUTUPSFEBTBSFRVFTUQBUI

    View Slide

  134. module SafeRuby

    PARSER_REGEX = /["'](?\/.*)["']\.(?[A-z]+)/

    end

    Toycol::Protocol.de
    fi
    ne(:safe_ruby) do |message|

    using Module.new {

    re
    fi
    ne String do

    def get

    Toycol::Protocol.request.http_method { “GET" }

    end

    end

    }

    path, method = SafeRuby::PARSER_REGEX.match(message) { |m| [m[:path], m[:method]] }

    request.path { path }

    request_path.public_send(method)

    end
    1SPUPDPM
    fi
    [email protected]
    4BGF3VCZQSPUPDPM"EEHFUUP4USJOHDMBTT
    "EEHFUNFUIPEUPUIF4USJOHDMBTT
    5IJTJTUPTUPSFBTUSJOH(&5JO
    5PZD[email protected]

    View Slide

  135. module SafeRuby

    PARSER_REGEX = /["'](?\/.*)["']\.(?[A-z]+)/

    end

    Toycol::Protocol.de
    fi
    ne(:safe_ruby) do |message|

    using Module.new {

    re
    fi
    ne String do

    def get

    Toycol::Protocol.request.http_method { “GET" }

    end

    end

    }

    path, method = SafeRuby::PARSER_REGEX.match(message) { |m| [m[:path], m[:method]] }

    request.path { path }

    request_path.public_send(method)

    end
    1SPUPDPM
    fi
    [email protected]
    4BGF3VCZQSPUPDPM$BMMHFUGPSlQPTUTz
    $BMMUIFHFUNFUIPEPOUIF4USJOHPCKFDUlQPTUTz
    &YFDVUJOHUIJTMJOFXJMMHJWFZPVUIFTBNFSFTVMU
    BTFYFDVUJOHUIFSFRVFTUNFTTBHFlQPTUTzHFU

    View Slide

  136. -FUTUSZUIJTPVU

    View Slide

  137. require "rack"

    require "toycol"

    Toycol::Protocol.use(:safe_ruby)

    class App

    def call(env)

    case env["REQUEST_METHOD"]

    when “GET”

    [200, { "Content-Type" => "text/html" },

    [“User<1> I love Ruby!\n”, “User<2> I love RubyKaigi!\n”]]

    end

    end

    end

    run App.new )FSFJTBOBQQMJDBUJPO
    fi
    MFGPSUIJTQSPKFDU
    6TFS
    DPO
    fi
    [email protected]@SVCZSV
    3VO4BGF3VCZQSPUPDPM

    View Slide

  138. 4FOEBSFRVFTUNFTTBHFCZ4BGF3VCZQSPUPDPM
    4UBSUVQUIFTFSWFST
    3VO4BGF3VCZQSPUPDPM

    View Slide

  139. *UXPSLFE
    3VO4BGF3VCZQSPUPDPM

    View Slide

  140. /PX MFUTUSZ4BGF3VCZQSPUPDPMJO
    TPNFBQQMJDBUJPOTCVJMUXJUIPUIFSXBZT

    View Slide

  141. 8IBUBCPVUBOBQQMJDBUJPOCVJMUXJUI
    4JOBUSB

    View Slide

  142. require “sinatra/base"

    require "toycol"

    Toycol::Protocol.use(:safe_ruby)

    class App < Sinatra::Base

    get “/posts” do

    [“User<1> I love Ruby!\n”, “User<2> I love RubyKaigi!\n”]

    end

    run! if app_
    fi
    le == $PROGRAM_NAME

    end
    6TFS
    BQQSC
    3VO4BGF3VCZQSPUPDPMXJUIB4JOBUSBBQQ
    4JOBUSBBQQGPS4BGF3VCZQSPUPDPM

    View Slide

  143. 3VO4BGF3VCZQSPUPDPMXJUIB4JOBUSBBQQ
    4FOEBSFRVFTUNFTTBHFCZ4BGF3VCZQSPUPDPM
    4UBSUVQUIFTFSWFST

    View Slide

  144. "HBJO JUXPSLFE
    3VO4BGF3VCZQSPUPDPMXJUIB4JOBUSBBQQ

    View Slide

  145. 'JOBMMZ MFUTHFUB3BJMTBQQMJDBUJPO
    SVOOJOHXJUIPVS4BGF3VCZQSPUPDPM

    View Slide

  146. require_relative “con
    fi
    g/environment"

    require "toycol"

    Toycol::Protocol.use(:safe_ruby)

    run! Rails.application

    Rails.application.load_server

    6TFS
    DPO
    fi
    HSV
    4BGF3VCZQSPUPDPMXJUIB3BJMTBQQ
    "3BJMTBQQGPS4BGF3VCZQSPUPDPM
    SBJMTHFOFSBUFTDB
    ff
    PMEQPTUT
    DPNNBOEIBTCFFOFYFDVUFE

    View Slide

  147. 4BGF3VCZQSPUPDPMXJUIB3BJMTBQQ
    4FOEBSFRVFTUNFTTBHFCZ4BGF3VCZQSPUPDPM
    4UBSUVQUIFTFSWFST

    View Slide

  148. "OEBHBJO JUXPSLFE
    XJUIMPUTPGIFBEFST

    4BGF3VCZQSPUPDPMXJUIB3BJMTBQQ
    ʜ

    View Slide

  149. :PVDBOBMTPTFFUIFCPEZ
    4BGF3VCZQSPUPDPMXJUIB3BJMTBQQ

    View Slide

  150. "TZPVDBOTFF XFXFSFBCMFUPSVO
    BWBSJFUZPGBQQMJDBUJPOT
    XJUI4BGF3VCZQSPUPDPM

    View Slide

  151. *OBEEJUJPO
    ZPVDBO
    fi
    OEFYBNQMFTPG
    HFUUJOHRVFSZQBSBNFUFST
    BOEIBOEMJOH1045NFUIPET
    JOUIF5PZDPMSFQPTJUPSZ
    1MFBTFFOKPZUIFNJGZPVMJLF
    IUUQTHJUIVCDPNTIJPJNNUPZDPMCMPCNBJOFYBNQMFT

    View Slide

  152. "OETQFDJBMUIBOLTUP
    !PLVSBNBTBGVNJ
    !VE[VSB'VLVPLBSC
    5IBOLZPVGPSZPVSBUUFOUJPO

    View Slide