/FUXPSLOPEF Application Layer Transport Layer Internet Layer Link Layer 5IFGBDUUIBUMBZFSTPGUIFTBNFMFWFMPGUIFBCTUSBDUJPO DPNNVOJDBUFXJUIFBDIPUIFSVTJOHUIFTBNFQSPUPDPM
[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
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
'PSEF fi OJOHQSPUPDPM 5IJT fi MFOFFETUPCFOBNFE l1SPUPDPM fi MFzPSl1SPUPDPM fi MFSVCZMJLFzBOETPPO "3BDLDPNQBUJCMFBQQMJDBUJPO FHDPO fi HSV ᶃ1SFQBSFBDPO fi HVSBUJPO fi MFBBQQMJDBUJPO
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
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
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
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
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
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
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
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
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
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
--- [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
= {}) @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
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
= @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
@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
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
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
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
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
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
ʜ EFGTFMGEF fi OF QSPUPDPM@OBNFEFGBVMU CMPDL ʜ !EF fi OFNFOUT<QSPUPDPM@OBNF>CMPDL FOE ʜ FOE FOE MJCUPZDPMQSPUPDPMSC "TTJHOBIBTIPCKFDUUPBJOTUBODFWBSJBCMF!EF fi OFNFOUT 4UPSFUIFQSPUPDPMOBNFBTUIFLFZBOEUIFCMPDLEF fi OJOH UIFQSPUPDPMBTUIFWBMVFJOUIJTIBTIPCKFDU
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
!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
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
= @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
= @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
= @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
@input if @input request_message end … end end MJCUPZDPMQSPYZSC 5PZDPM1SPYZCVJME@IUUQ@SFRVFTU@NFTTBHF#VJMEBSFRVFTUNFTTBHF 5PZDPM1SPYZCVJME@IUUQ@SFRVFTU@NFTTBHFCVJMET B)551GPSNBUUFESFRVFTUNFTTBHF VTJOHUIPTFJOTUBODFWBSJBCMFT
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
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
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
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
@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
|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
|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ɹ
|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
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
|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
|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 ᶃ ᶄ
|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
|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
|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
|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
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
“/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