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. 8IBU*MJLFBCPVU5$1*1QSPUPDPMTUBDLᶄ /FUXPSLOPEF Application Layer Transport Layer Internet Layer Link Layer

    /FUXPSLOPEF Application Layer Transport Layer Internet Layer Link Layer  5IFGBDUUIBUMBZFSTPGUIFTBNFMFWFMPGUIFBCTUSBDUJPO DPNNVOJDBUFXJUIFBDIPUIFSVTJOHUIFTBNFQSPUPDPM
  2. '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
  3. '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 ˕
  4. $ 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
  5. $ 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
  6. 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
  7. ᶃ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
  8. ᶄ%F fi OFZPVSQSPUPDPMJOUIF1SPUPDPM fi MF Toycol::Protocol.de fi ne(:rubylike) do request.path

    do |message| /['"](?<path>.+)['"]/.match(message)[:path] end request.http_method do |message| case /\.(?<method>[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
  9. Toycol::Protocol.de fi ne(:rubylike) do request.path do |message| /['"](?<path>.+)['"]/.match(message)[:path] end request.http_method

    do |message| case /\.(?<method>[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
  10. Toycol::Protocol.de fi ne(:rubylike) do request.path do |message| /['"](?<path>.+)['"]/.match(message)[:path] end request.http_method

    do |message| case /\.(?<method>[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 SFRVFTUQBUISFRVFTUIUUQ@NFUIPE ᶄ%F fi OFZPVSQSPUPDPMJOUIF1SPUPDPM fi MF
  11. Toycol::Protocol.de fi ne(:rubylike) do request.path do |message| /['"](?<path>.+)['"]/.match(message)[:path] end request.http_method

    do |message| case /\.(?<method>[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 CZBEEJUJPOBM@SFRVFTU@NFUIPET BEEOFXSFRVFTUNFUIPETCZ DVTUPN@TUBUVT@DPEFTNFUIPE ᶄ%F fi OFZPVSQSPUPDPMJOUIF1SPUPDPM fi MF
  12. Toycol::Protocol.de fi ne(:rubylike) do request.path do |message| /['"](?<path>.+)['"]/.match(message)[:path] end request.http_method

    do |message| case /\.(?<method>[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
  13. Toycol::Protocol.de fi ne(:rubylike) do request.path do |message| /['"](?<path>.+)['"]/.match(message)[:path] end request.http_method

    do |message| case /\.(?<method>[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
  14. ᶅ#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 H@SVCZJMLFSV /FYU CVJMEB3BDLDPNQBUJCMFBQQMJDBUJPO UIBUSVOTPO3VCZMJLFQSPUPDPM
  15. 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 H@SVCZJMLFSV ᶅ#VJMEB3BDLDPNQBUJCMFBQQMJDBUJPO
  16. 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 H@SVCZJMLFSV ᶅ#VJMEB3BDLDPNQBUJCMFBQQMJDBUJPO
  17. -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
  18. -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
  19. 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
  20. 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
  21. 3BDL)BOEMFS5PZDPMTFMFDU@CBDLHSPVOE@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
  22. 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
  23. 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
  24. 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
  25. 5PZDPM1SPYZTUBSU$BMMUSBOTGFS@UP@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 $BMM5PZDPM1SPYZUSBOTGFS@UP@TFSWFS UIBUTFOETUIFSFRVFTUNFTTBHFUP UIFCBDLHSPVOETFSWFS
  26. 5PZDPM1SPYZUSBOTGFS@UP@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 5PZDPM1SPYZUSBOTGFS@UP@TFSWFS
  27. 5PZDPM1SPYZUSBOTGFS@UP@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
  28. 5PZDPM1SPYZUSBOTGFS@UP@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ʜ
  29. 5PZDPM1SPYZUSBOTGFS@UP@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
  30. 5PZDPM1SPYZUSBOTGFS@UP@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
  31. 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 H@SVCZJMLFSV *OUIFBQQMJDBUJPO fi MF  UPZDPMJTSFRVJSFEUPSVO5PZDPM
  32. 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
  33. 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
  34. 1SPUPDPM fi MF4UPSFUIFFOUJSFEF fi OJUJPO Toycol::Protocol.de fi ne(:rubylike) do request.path

    do |message| /['"](?<path>.+)['"]/.match(message)[:path] end request.http_method do |message| case /\.(?<method>[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
  35. 1SPUPDPM fi MF4UPSFUIFFOUJSFEF fi OJUJPO Toycol::Protocol.de fi ne(:rubylike) do request.path

    do |message| /['"](?<path>.+)['"]/.match(message)[:path] end request.http_method do |message| case /\.(?<method>[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
  36. 5PZDPM1SPUPDPMEF fi OF4UPSFUIFEF fi OJUJPO NPEVMF5PZDPM DMBTT1SPUPDPM !EF fi OFNFOUT\^

    ʜ EFGTFMGEF fi OF QSPUPDPM@OBNFEFGBVMU CMPDL  ʜ !EF fi OFNFOUT<QSPUPDPM@OBNF>CMPDL FOE ʜ FOE FOE MJCUPZDPMQSPUPDPMSC 5PZDPM1SPUPDPMEF fi OF
  37. 5PZDPM1SPUPDPMEF fi OF4UPSFUIFEF fi OJUJPO NPEVMF5PZDPM DMBTT1SPUPDPM !EF fi OFNFOUT\^

    ʜ EFGTFMGEF fi OF QSPUPDPM@OBNFEFGBVMU CMPDL  ʜ !EF fi OFNFOUT<QSPUPDPM@OBNF>CMPDL FOE ʜ FOE FOE MJCUPZDPMQSPUPDPMSC "TTJHOBIBTIPCKFDUUPBJOTUBODFWBSJBCMF!EF fi OFNFOUT 4UPSFUIFQSPUPDPMOBNFBTUIFLFZBOEUIFCMPDLEF fi OJOH UIFQSPUPDPMBTUIFWBMVFJOUIJTIBTIPCKFDU
  38. 1SPUPDPM fi MF4UPSFBEFUBJMFEEF fi OJUJPOGPSFBDI Toycol::Protocol.de fi ne(:rubylike) do request.path

    do |message| /['"](?<path>.+)['"]/.match(message)[:path] end request.http_method do |message| case /\.(?<method>[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
  39. NPEVMF5PZDPM DMBTT1SPUPDPM ʜ EFGTFMGSFRVFTU !SFRVFTUcc$MBTTOFXEP EFGTFMGQBUI CMPDL  !QBUICMPDL FOE

    EFGTFMGIUUQ@NFUIPE CMPDL  !IUUQ@NFUIPECMPDL FOE ʜ FOE FOE ʜ FOE FOE FH5PZDPM1SPUPDPMSFRVFTU4UPSFUIFEF fi OJUJPO MJCUPZDPMQSPUPDPMSC 'PSFYBNQMF5PZDPM1SPUPDPMSFRVFTU
  40. NPEVMF5PZDPM DMBTT1SPUPDPM ʜ EFGTFMGSFRVFTU !SFRVFTUcc$MBTTOFXEP EFGTFMGQBUI CMPDL  !QBUICMPDL FOE

    EFGTFMGIUUQ@NFUIPE CMPDL  !IUUQ@NFUIPECMPDL FOE ʜ FOE FOE ʜ FOE FOE FH5PZDPM1SPUPDPMSFRVFTU4UPSFUIFEF fi OJUJPO MJCUPZDPMQSPUPDPMSC 5IJTJTGPSBTTJHOJOHBOBOPOZNPVTDMBTTUP !SFRVFTUPG5PZDPM1SPUPDPM 5IJTBOPOZNPVTDMBTTJTVTFEUPTUPSFFBDIMPHJDGPS QBSTJOHUIFSFRVFTUNFTTBHFJOJUTPXOJOTUBODFWBSJBCMF
  41. FH5PZDPM1SPUPDPMSFRVFTU4UPSFUIFEF fi OJUJPO NPEVMF5PZDPM DMBTT1SPUPDPM ʜ EFGTFMGSFRVFTU !SFRVFTUcc$MBTTOFXEP EFGTFMGQBUI CMPDL

     !QBUICMPDL FOE EFGTFMGIUUQ@NFUIPE CMPDL  !IUUQ@NFUIPECMPDL FOE ʜ FOE FOE ʜ FOE FOE MJCUPZDPMQSPUPDPMSC &BDIJOTUBODFWBSJBCMFPGUIJTBOPOZNPVT DMBTTTUPSFTBCMPDLPGMPHJDUPHFU UIFSFRVFTUQBUI )551NFUIPE FUD SFRVFTUQBUIEPcNFTTBHFc /['"](?<path>.+)['"]/.match(message)[:path] end request.http_method do |message| case /\.(?<method>[A-z]+)/.match(message)&.captures&. fi rst when "get" then "GET" else "OTHER" end end  6TFS 1SPUPDPM fi MFSVCZMJLF
  42. FH5PZDPM1SPUPDPMSFRVFTU4UPSFUIFEF fi OJUJPO NPEVMF5PZDPM DMBTT1SPUPDPM ʜ EFGTFMGSFRVFTU !SFRVFTUcc$MBTTOFXEP EFGTFMGQBUI CMPDL

     !QBUICMPDL FOE EFGTFMGIUUQ@NFUIPE CMPDL  !IUUQ@NFUIPECMPDL FOE ʜ FOE FOE ʜ FOE FOE MJCUPZDPMQSPUPDPMSC :PVDBOSFUSJFWFUIFTUPSFEMPHJDTCZFYFDVUJOHDBMMNFUIPE POFBDICMPDLTUPSFEJOUIFTFWBSJBCMFT 5PZDPM1SPUPDPMDMBTTIBTTPNFJOUFSGBDFTUIBU BMMPXTUIFTFUPCFDBMMFEBUBOZUJNF
  43. FH5PZDPM1SPUPDPMSFRVFTU@QBUI3FUSJFWFUIFEF fi OJUJPO NPEVMF5PZDPM DMBTT1SPUPDPM ʜ EFGTFMGSFRVFTU@QBUI SFRVFTU@QBUISFRVFTUJOTUBODF@WBSJBCMF@HFU l!QBUIz DBMM

    SFRVFTU@NFTTBHF  ʜ SFRVFTU@QBUI FOE FOE FOE MJCUPZDPMQSPUPDPMSC 'PSFYBNQMF5PZDPM1SPUPDPMSFRVFTU@QBUI
  44. FH5PZDPM1SPUPDPMSFRVFTU@QBUI3FUSJFWFUIFEF fi OJUJPO NPEVMF5PZDPM DMBTT1SPUPDPM ʜ EFGTFMGSFRVFTU@QBUI SFRVFTU@QBUISFRVFTUJOTUBODF@WBSJBCMF@HFU l!QBUIz DBMM

    SFRVFTU@NFTTBHF  ʜ SFRVFTU@QBUI FOE FOE FOE MJCUPZDPMQSPUPDPMSC 5IJTJTUIFNFUIPEUPHFUUIFSFRVFTUQBUI GSPNBSFRVFTUNFTTBHFBOE BMPHJDUPQBSTFUIFSFRVFTUNFTTBHF
  45. FH5PZDPM1SPUPDPMSFRVFTU@QBUI3FUSJFWFUIFEF fi OJUJPO NPEVMF5PZDPM DMBTT1SPUPDPM ʜ EFGTFMGSFRVFTU@QBUI SFRVFTU@QBUISFRVFTUJOTUBODF@WBSJBCMF@HFU l!QBUIz DBMM

    SFRVFTU@NFTTBHF  ʜ SFRVFTU@QBUI FOE FOE FOE MJCUPZDPMQSPUPDPMSC EFGBTTJHO@QBSTFE@BUUSJCVUFT ʜ !QBUI!QSPUPDPMSFRVFTU@QBUI ʜ FOE MJCUPZDPMQSPYZSC 5IFQSPYZTFSWFSVTFTUIJTNFUIPEUPSFUSJFWF UIFSFRVFTUQBUI XIFOUIFQSPYZTFSWFSCVJMET B)551GPSNBUUFESFRVFTUNFTTBHF
  46. "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 H@SVCZJMLFSV 1BTTUIFQSPUPDPMOBNFZPVXBOUUPVTFUP 5PZDPM1SPUPDPMVTF
  47. 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
  48. 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
  49. NPEVMF5PZDPM DMBTT1SPUPDPM ʜ EFGTFMGSVO NFTTBHF  !SFRVFTU@NFTTBHFNFTTBHFDIPNQ SFUVSOVOMFTT CMPDL!EF fi

    OFNFOUT<!QSPUPDPM@OBNF>  JOTUBODF@FYFD !SFRVFTU@NFTTBHF CMPDL  FOE ʜ FOE FOE 5PZDPM1SPUPDPMSVO"QQMZUIFQSPUPDPM MJCUPZDPMQSPUPDPMSC 5PZDPM1SPUPDPMSVO
  50. NPEVMF5PZDPM DMBTT1SPUPDPM ʜ EFGTFMGSVO NFTTBHF  !SFRVFTU@NFTTBHFNFTTBHFDIPNQ SFUVSOVOMFTT CMPDL!EF fi

    OFNFOUT<!QSPUPDPM@OBNF>  JOTUBODF@FYFD !SFRVFTU@NFTTBHF CMPDL  FOE ʜ FOE FOE 5PZDPM1SPYZTUBSU"QQMZUIFQSPUPDPM MJCUPZDPMQSPUPDPMSC 'JOEUIFQSPUPDPMEF fi OJUJPO DPSSFTQPOEJOHUP!QSPUPDPM@OBNF JO!EF fi OFNFOUTIBTI
  51. NPEVMF5PZDPM DMBTT1SPUPDPM ʜ EFGTFMGSVO NFTTBHF  !SFRVFTU@NFTTBHFNFTTBHFDIPNQ SFUVSOVOMFTT CMPDL!EF fi

    OFNFOUT<!QSPUPDPM@OBNF>  JOTUBODF@FYFD !SFRVFTU@NFTTBHF CMPDL  FOE ʜ FOE FOE 5PZDPM1SPYZTUBSU"QQMZUIFQSPUPDPM MJCUPZDPMQSPUPDPMSC &YFDVUFUIJTCMPDLJOJOTUBODF@FYFD 5IFSFRVFTUNFTTBHFQBTTFEBT UIFBSHVNFOUXJMMCFUSBOTMBUFE
  52. 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 5IFOFYFDVUF5PZDPM1SPYZBTTJHO@QBSTFE@BUUSJCVUFT BOE5PZDPM1SPYZCVJME@IUUQ@SFRVFTU@NFTTBHFUP CVJMEB)551GPSNBUUFESFRVFTUNFTTBHF
  53. 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 5PZDPM1SPYZBTTJHO@QBSTFE@BUUSJCVUFT#VJMEBSFRVFTUNFTTBHF 5PZDPM1SPYZBTTJHO@QBSTFE@BUUSJCVUFTFYUSBDUT WBMVFTTUPSFEJO5PZDPM1SPUPDPMDMBTT "OEBTTJHOTUIFNUPFBDIJOTUBODFWBSJBCMFT
  54. 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 5PZDPM1SPYZCVJME@IUUQ@SFRVFTU@NFTTBHF#VJMEBSFRVFTUNFTTBHF 5PZDPM1SPYZCVJME@IUUQ@SFRVFTU@NFTTBHFCVJMET B)551GPSNBUUFESFRVFTUNFTTBHF VTJOHUIPTFJOTUBODFWBSJBCMFT
  55. 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 5PZDPM1SPYZCVJME@IUUQ@SFRVFTU@NFTTBHF#VJMEBSFRVFTUNFTTBHF FH 5PZDPM1SPYZCVJME@IUUQ@SFRVFTU@NFTTBHFSFUVSOT 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"
  56. 3VCZMJLFQSPUPDPM1BSTJOHUIFTUSJOHlbQPTUT`HFUz Toycol::Protocol.de fi ne(:rubylike) do request.path do |message| /['"](?<path>.+)['"]/.match(message)[:path] end

    request.http_method do |message| case /\.(?<method>[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
  57. 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
  58. 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
  59. 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 5IFOVTFJOTUBODF@FWBMUPFYFDVUF UIFSFDFJWFESFRVFTUNFTTBHF lQPTUTzHFU  BTB3VCZTDSJQU lQPTUTzHFU
  60. 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 BCMPDLPGTBGF@FYFDVUJPOUPFOTVSFUIBU OPWVMOFSBCMFEF fi OJUJPOTBSFJO1SPUPDPM fi MFT
  61. 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)FMQFSTBGF@FYFDVUJPO MJCUPZDPMIFMQFSSC 5PZDPM)FMQFSTBGF@FYFDVUJPO  "OE5PZDPM)FMQFSTBGF@FYFDVUJPOBCMF@UQ UIBUTBGF@FYFDVUJPOVTFTJOUFSOBMMZ
  62. 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)FMQFSTBGF@FYFDVUJPO MJCUPZDPMIFMQFSSC 6TF5SBDF1PJOUUPEFUFDUJGBOZEBOHFSPVT NFUIPETBSFCFJOHFYFDVUFEUIBUDPVME DBVTFJOKFDUJPOWVMOFSBCJMJUJFTɹ
  63. 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)FMQFSTBGF@FYFDVUJPO MJCUPZDPMIFMQFSSC 5BSHFUNFUIPETBSFJODMVEJOHFWBMPSFYFD JOUIBUOBNF FYFDVUJOHFYUFSOBMDPNNBOET  BOEMPBEJOH fi MFTGSPNPVUTJEF
  64. 4BGF3VCZQSPUPDPM module SafeRuby PARSER_REGEX = /["'](?<path>\/.*)["']\.(?<method>[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 MFTBGF@SVCZ 4BGF3VCZQSPUPDPM
  65. module SafeRuby PARSER_REGEX = /["'](?<path>\/.*)["']\.(?<method>[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 MFTBGF@SVCZ 1SFQBSFBSFHVMBSFYQSFTTJPOUPQBSTF UIFSFRVFTUNFTTBHF
  66. module SafeRuby PARSER_REGEX = /["'](?<path>\/.*)["']\.(?<method>[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 MFTBGF@SVCZ ᶃ ᶄ
  67. module SafeRuby PARSER_REGEX = /["'](?<path>\/.*)["']\.(?<method>[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 MFTBGF@SVCZ 4BGF3VCZQSPUPDPM4UPSFUIFSFRVFTUQBUI 4UPSFlQPTUTzBTBSFRVFTUQBUI JO5PZDPM1SPUPDPMSFRVFTUQBUI
  68. module SafeRuby PARSER_REGEX = /["'](?<path>\/.*)["']\.(?<method>[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 MFTBGF@SVCZ 4BGF3VCZQSPUPDPM3FUSJFWFUIFSFRVFTUQBUI 5IFO 5PZDPM1SPUPDPMSFRVFTU@QBUI SFUVSOTB4USJOHPCKFDUlQPTUTz KVTUTUPSFEBTBSFRVFTUQBUI
  69. module SafeRuby PARSER_REGEX = /["'](?<path>\/.*)["']\.(?<method>[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 MFTBGF@SVCZ 4BGF3VCZQSPUPDPM"EEHFUUP4USJOHDMBTT "EEHFUNFUIPEUPUIF4USJOHDMBTT 5IJTJTUPTUPSFBTUSJOH(&5JO 5PZDPM1SPUPDPMSFRVFTUIUUQ@NFUIPE
  70. module SafeRuby PARSER_REGEX = /["'](?<path>\/.*)["']\.(?<method>[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 MFTBGF@SVCZ 4BGF3VCZQSPUPDPM$BMMHFUGPSlQPTUTz $BMMUIFHFUNFUIPEPOUIF4USJOHPCKFDUlQPTUTz &YFDVUJOHUIJTMJOFXJMMHJWFZPVUIFTBNFSFTVMU BTFYFDVUJOHUIFSFRVFTUNFTTBHFlQPTUTzHFU
  71. 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 H@TBGF@SVCZSV 3VO4BGF3VCZQSPUPDPM
  72. 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
  73. 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