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

RailsConf 2022 Keynote

RailsConf 2022 Keynote

This is my RailsConf 2022 keynote

Aaron Patterson

June 01, 2022
Tweet

More Decks by Aaron Patterson

Other Decks in Technology

Transcript

  1. View Slide

  2. RailsConf 2022
    It's been a minute!

    View Slide

  3. PORTLAND

    View Slide

  4. View Slide

  5. View Slide

  6. View Slide

  7. View Slide

  8. View Slide

  9. Couch
    Glisan

    View Slide

  10. View Slide

  11. 3 YEARS!

    View Slide

  12. #FridayHug

    View Slide

  13. 🇺🇦🇺🇦🇺🇦

    View Slide

  14. View Slide

  15. View Slide

  16. RIP Little Buddy 😭

    View Slide

  17. View Slide

  18. ~5 Photos / Day 🤣

    View Slide

  19. View Slide

  20. View Slide

  21. View Slide

  22. View Slide

  23. View Slide

  24. View Slide

  25. https://www.youtube.com/
    TenderlovesCoolStuff
    Subscribe

    View Slide

  26. Tenderlove's Cool Stuff

    View Slide

  27. View Slide

  28. ˑˑˑˑˑ


    0 Stars: Do Not Recommend

    View Slide

  29. 💨

    View Slide

  30. View Slide

  31. JIT: Generate Machine Code at
    Runtime

    View Slide

  32. Machine Code:


    Sequence of Bytes

    View Slide

  33. Ruby:


    Can Make a Sequence of Bytes

    View Slide

  34. 🤔 Pure Ruby JIT? 🤔

    View Slide

  35. Yes! Yes, we can.

    View Slide

  36. https://github.com/tenderlove/tenderjit

    View Slide

  37. TenderJIT

    View Slide

  38. TenderJIT

    View Slide

  39. "Just because you can, doesn't
    mean you should!"

    View Slide

  40. Analog Terminal Bell
    https://analogterminalbell.com

    View Slide

  41. Professional Software
    Development

    View Slide

  42. View Slide

  43. Apple M1: AArch64

    View Slide

  44. x86_64 Only

    View Slide

  45. LeJIT JITs

    View Slide

  46. AArch64 Gem
    https://github.com/tenderlove/aarch64

    View Slide

  47. AArch64 Example
    JIT a function that returns 0xF00DCAFE
    $ gel exec ruby test.rb


    "f00dcafe"
    Terminal Output
    require "aarch64"


    require "jit_buffer"


    # create a JIT buffer


    jit_buffer = JITBuffer.new 4096


    asm = AArch64::Assembler.new


    # Make some instructions


    asm.pretty do


    asm.movz x0, 0xCAFE


    asm.movk x0, 0xF00D, lsl(16)


    asm.ret


    end


    # Write the instructions to a JIT buffer


    jit_buffer.writeable!


    asm.write_to jit_buffer


    jit_buffer.executable!


    # Execute the JIT buffer


    p jit_buffer.to_function([], -Fiddle::TYPE_INT).call.to_s(16) #
    => f00dcafe
    JIT Code

    View Slide

  48. AArch64 Example
    JIT a function that returns 0xF00DCAFE
    $ gel exec ruby test.rb


    "f00dcafe"
    Terminal Output
    asm = AArch64::Assembler.new


    # Make some instructions


    asm.pretty do


    asm.movz x0, 0xCAFE


    asm.movk x0, 0xF00D, lsl(16)


    asm.ret


    end
    JIT Code

    View Slide

  49. Not Amateur Enough

    View Slide

  50. Extremely Amateur

    View Slide

  51. AArch64 Example
    JIT a function that returns 0xF00DCAFE
    $ gel exec ruby test.rb


    "f00dcafe"
    Terminal Output
    require "aarch64"


    require "jit_buffer"


    # create a JIT buffer


    jit_buffer = JITBuffer.new 4096


    asm = AArch64::Assembler.new


    # Make some instructions


    asm.pretty do


    asm.movz x0, 0xCAFE


    asm.movk x0, 0xF00D, lsl(16)


    asm.ret


    end


    # Write the instructions to a JIT buffer


    jit_buffer.writeable!


    asm.write_to jit_buffer


    jit_buffer.executable!


    # Execute the JIT buffer


    p jit_buffer.to_function([], -Fiddle::TYPE_INT).call.to_s(16) #
    => f00dcafe
    JIT Code

    View Slide

  52. AArch64 Example
    JIT a function that returns 0xF00DCAFE
    $ gel exec ruby test.rb


    "f00dcafe"
    Terminal Output
    require "aarch64"


    require "jit_buffer"


    # create a JIT buffer


    jit_buffer = JITBuffer.new 4096


    asm = AArch64::Assembler.new


    # Make some instructions


    asm.pretty do


    asm.movz x0, 0xCAFE


    asm.movk x0, 0xF00D, lsl(16)


    asm.ret


    end


    # Write the instructions to a JIT buffer


    jit_buffer.writeable!


    asm.write_to jit_buffer


    jit_buffer.executable!


    # Execute the JIT buffer


    p jit_buffer.to_function([], -Fiddle::TYPE_INT).call.to_s(16) #
    => f00dcafe
    JIT Code
    JITBuffer.new

    View Slide

  53. JITBuffer:


    Executable Memory

    View Slide

  54. Linux / macOS

    View Slide

  55. Any Platform

    View Slide

  56. Test Code Execution
    Write some bytes and ensure it can execute them
    # create a JIT buffer


    jit_buffer = JITBuffer.new 4096


    # Write the instructions to a JIT buffer


    jit_buffer.writeable!


    jit_buffer.write asm.to_binary


    jit_buffer.executable!


    # Execute the JIT buffer


    p jit_buffer.to_function([],


    -Fiddle::TYPE_INT).call

    View Slide

  57. Test on x86_64 and ARM

    View Slide

  58. Test Code Execution
    Add a check for the current platform
    # create a JIT buffer


    jit_buffer = JITBuffer.new 4096


    # Write the instructions to a JIT buffer


    jit_buffer.writeable!


    if arm64?


    jit_buffer.write arm64_bytes


    else


    jit_buffer.write x86_bytes


    end


    jit_buffer.executable!

    View Slide

  59. Test Code Execution
    Add a check for the current platform
    # create a JIT buffer


    jit_buffer = JITBuffer.new 4096


    # Write the instructions to a JIT buffer


    jit_buffer.writeable!


    if arm64?


    jit_buffer.write arm64_bytes


    else


    jit_buffer.write x86_bytes


    end


    jit_buffer.executable!

    View Slide

  60. If Statements Mean "Complexity"

    View Slide

  61. Not "Cool"

    View Slide

  62. Can we ask the CPU itself?

    View Slide

  63. Machine Code is Just Bytes

    View Slide

  64. Bytes Valid for


    x86_64 and ARM64?

    View Slide

  65. YES!

    View Slide

  66. Worst Best Idea Ever!

    View Slide

  67. x86 Machine Code
    Very Cool Code (Intel Syntax)
    .foo:


    mov rax, 0x2b


    ret


    jmp foo
    Put 0x2b in the
    "RAX" register
    Return from the
    current function
    Jump to the "foo"
    label???

    View Slide

  68. x86 Machine Code Bytes
    Bytes are in base 16
    48 C7 C0 2B 00 00 00 mov rax, 0x2b
    C3 ret
    EB F6 jmp foo
    [0x48, 0xc7, 0xc0, 0x2b, 0x00, 0x00, 0x00, 0xc3, 0xeb, 0xf6]


    View Slide

  69. ARM64 Machine Code
    Very Very Cool Code
    movz X11, 0x7b7


    movz X0, 0x2b


    ret
    Write 0x7b7 to
    X11 register
    because???
    Write 0x2b to the
    X0 register
    Return from the
    current function

    View Slide

  70. ARM64 Machine Code Bytes
    Bytes are in base 16
    EB F6 80 D2 movz x11, 0x7b7
    60 05 80 D2 movz x0, 0x2b
    C0 03 5F D6 ret

    View Slide

  71. Machine Code for x86 and ARM64
    x86_64 Machine Code
    48 C7 C0 2B 00 00 00 mov rax, 0x2b
    C3 ret
    EB F6 jmp foo
    ARM64 Machine Code
    EB F6 80 D2 movz x11, 0x7b7
    60 05 80 D2 movz x0, 0x2b
    C0 03 5F D6 ret

    View Slide

  72. Machine Code for x86 and ARM64
    x86_64 Machine Code
    48 C7 C0 2B 00 00 00 mov rax, 0x2b
    C3 ret
    EB F6 jmp foo
    ARM64 Machine Code
    EB F6 80 D2 movz x11, 0x7b7
    60 05 80 D2 movz x0, 0x2b
    C0 03 5F D6 ret

    View Slide

  73. Machine Code for x86 and ARM64
    48 C7 C0 2B 00 00 00 mov rax, 0x2b
    C3 ret
    EB F6 jmp foo
    EB F6 80 D2 movz x11, 0x7b7
    60 05 80 D2 movz x0, 0x2b
    C0 03 5F D6 ret

    View Slide

  74. Machine Code for x86 and ARM64
    48 C7 C0 2B 00 00 00 mov rax, 0x2b
    C3 ret
    EB F6 jmp foo
    EB F6 80 D2
    60 05 80 D2 movz x0, 0x2b
    C0 03 5F D6 ret

    View Slide

  75. Machine Code for x86 and ARM64
    48 C7 C0 2B 00 00 00 mov rax, 0x2b
    C3 ret
    EB F6
    EB F6 80 D2 movz x11, 0x7b7
    60 05 80 D2 movz x0, 0x2b
    C0 03 5F D6 ret

    View Slide

  76. Both Return 0x2B

    View Slide

  77. JITBuffer Test Case
    VCP (Very Cool Program)
    jit = JITBuffer.new 4096


    bytes = [0x48, 0xc7, 0xc0, 0x2b, 0x00, 0x00, 0x00, # x86_64 mov rax, 0x2b


    0xc3, # x86_64 ret


    0xeb, 0xf6, # x86 jmp


    0x80, 0xd2, # ARM movz X11, 0x7b7


    0x60, 0x05, 0x80, 0xd2, # ARM movz X0, #0x2b


    0xc0, 0x03, 0x5f, 0xd6] # ARM ret


    jit.writeable!


    jit.write bytes.pack("C*")


    jit.executable!


    func = Fiddle::Function.new(jit.to_i + 8, [], Fiddle::TYPE_INT)


    assert_equal 0x2b, func.call
    Start at
    offset 8
    Offset 8
    Alw
    ays
    0x2b!!

    View Slide

  78. Cross Platform!!!!!*
    *x86_64 and ARM64 only

    View Slide

  79. No If Statements!

    View Slide

  80. Complexity: 0

    View Slide

  81. So Simple!

    View Slide

  82. "Just because you can, doesn't
    mean you should!"

    View Slide

  83. View Slide

  84. Anatomy of a Tenderlove Talk

    View Slide

  85. Tenderlove Talk Timeline Teardown
    Jokes about local
    businesses
    Pictures of Cats
    W
    eird
    code
    not
    related
    to
    the
    topic
    M
    ain
    topic
    Abrupt ending
    after
    running
    out of tim
    e

    View Slide

  86. Talk Timeline Breakdown
    Jokes about local
    businesses
    Pictures of Cats
    W
    eird
    code
    not
    related
    to
    the
    topic
    M
    ain
    topic
    Abrupt ending
    after
    running
    out of tim
    e

    View Slide

  87. New Features in Rails 7!!
    Rack

    View Slide

  88. Spring
    It is gone!

    View Slide

  89. View Slide

  90. New Features in Rails 7
    Cool!
    • Import maps


    • Hotwire Default


    • Turbo 7 Default


    • Stimulus 3 Default


    • JS Bundling


    • CSS Bundling

    View Slide

  91. View Slide

  92. I am not a
    Front End Developer

    View Slide

  93. No Idea
    what these words mean

    View Slide

  94. But It's OK

    View Slide

  95. Import Maps and ES Modules

    View Slide

  96. Sprockets
    Webpacker
    ESM + Import Maps

    View Slide

  97. 🫠🫠🫠🫠
    "yay"

    View Slide

  98. New application.html.erb
    javascript_importmap_tags









    Myapp





    <%= csrf_meta_tags %>


    <%= csp_meta_tag %>


    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>


    <%= javascript_importmap_tags %>








    <%= yield %>






    View Slide

  99. Define importmaps
    con
    fi
    g/importmap.rb
    # Pin npm packages by running ./bin/importmap


    pin "application", preload: true


    pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true


    pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true


    pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true


    pin_all_from "app/javascript/controllers", under: "controllers"

    View Slide

  100. Rendered Application Layout
    Import map automatically generated
    {<br/><br/><br/>"imports": {<br/><br/><br/>"application": "/assets/application-123.js",<br/><br/><br/>"@hotwired/turbo-rails": "/assets/turbo.min-123.js",<br/><br/><br/>"@hotwired/stimulus": "/assets/stimulus.min-123.js",<br/><br/><br/>"@hotwired/stimulus-loading": "/assets/stimulus-loading-123.js",<br/><br/><br/>"controllers/application": "/assets/controllers/application-123.js",<br/><br/><br/>"controllers/hello_controller": "/assets/controllers/hello_controller-123.js",<br/><br/><br/>"controllers": "/assets/controllers/index-123.js"<br/><br/><br/>}<br/><br/><br/>}

    View Slide

  101. Pinning Libraries

    View Slide

  102. Put a pin in it!
    Add stu
    ff
    to the import map
    $ bin/importmap pin [email protected]/startCase.js


    Pinning "lodash-es/startCase.js" to https://ga.jspm.io/npm:[email protected]/startCase.js
    es: Español

    View Slide

  103. New config/importmap.rb
    With lodash-es
    # Pin npm packages by running ./bin/importmap


    pin "application", preload: true


    pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true


    pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true


    pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true


    pin_all_from "app/javascript/controllers", under: "controllers"


    pin "lodash-es/startCase.js", to: "https://ga.jspm.io/npm:[email protected]/startCase.js"

    View Slide

  104. New config/importmap.rb
    With lodash-es
    # Pin npm packages by running ./bin/importmap


    pin "application", preload: true


    pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true


    pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true


    pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true


    pin_all_from "app/javascript/controllers", under: "controllers"


    pin "lodash-es/startCase.js", to: "https://ga.jspm.io/npm:[email protected]/startCase.js"

    View Slide

  105. New config/importmap.rb
    With lodash-es
    # Pin npm packages by running ./bin/importmap


    pin "application", preload: true


    pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true


    pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true


    pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true


    pin_all_from "app/javascript/controllers", under: "controllers"


    pin "@lodash/startCase", to: "https://ga.jspm.io/npm:[email protected]/startCase.js"

    View Slide

  106. New Sourcemap
    Has Lowdash!
    {<br/><br/><br/>"imports": {<br/><br/><br/>"application": "/assets/application-123.js",<br/><br/><br/>"@hotwired/turbo-rails": "/assets/turbo.min-123.js",<br/><br/><br/>"@hotwired/stimulus": "/assets/stimulus.min-123.js",<br/><br/><br/>"@hotwired/stimulus-loading": "/assets/stimulus-loading-123.js",<br/><br/><br/>"@lodash/startCase": "https://ga.jspm.io/npm:[email protected]/startCase.js",<br/><br/><br/>"controllers/application": "/assets/controllers/application-123.js",<br/><br/><br/>"controllers/hello_controller": "/assets/controllers/hello_controller-123.js",<br/><br/><br/>"controllers": "/assets/controllers/index-123.js"<br/><br/><br/>}<br/><br/><br/>}

    View Slide

  107. New Sourcemap
    Has Lowdash!
    {<br/><br/><br/>"imports": {<br/><br/><br/>"application": "/assets/application-123.js",<br/><br/><br/>"@hotwired/turbo-rails": "/assets/turbo.min-123.js",<br/><br/><br/>"@hotwired/stimulus": "/assets/stimulus.min-123.js",<br/><br/><br/>"@hotwired/stimulus-loading": "/assets/stimulus-loading-123.js",<br/><br/><br/>"@lodash/startCase": "https://ga.jspm.io/npm:[email protected]/startCase.js",<br/><br/><br/>"controllers/application": "/assets/controllers/application-123.js",<br/><br/><br/>"controllers/hello_controller": "/assets/controllers/hello_controller-123.js",<br/><br/><br/>"controllers": "/assets/controllers/index-123.js"<br/><br/><br/>}<br/><br/><br/>}

    View Slide

  108. Use the new module!
    <br/><br/><br/>import startCase from '@lodash/startCase';<br/><br/><br/>const el = document.createElement('h1');<br/><br/><br/>const words = "hello, mom!"<br/><br/><br/>const text = document.createTextNode(startCase(words));<br/><br/><br/>el.appendChild(text);<br/><br/><br/><br/><br/>document.body.appendChild(el);<br/><br/><br/>

    View Slide

  109. View Slide

  110. Great!

    View Slide

  111. Indirect Names
    Use names rather than
    fi
    les
    {<br/><br/><br/>"imports": {<br/><br/><br/>"application": "/assets/application-123.js",<br/><br/><br/>"@hotwired/turbo-rails": "/assets/turbo.min-123.js",<br/><br/><br/>"@hotwired/stimulus": "/assets/stimulus.min-123.js",<br/><br/><br/>"@hotwired/stimulus-loading": "/assets/stimulus-loading-123.js",<br/><br/><br/>"controllers/application": "/assets/controllers/application-123.js",<br/><br/><br/>"controllers/hello_controller": "/assets/controllers/hello_controller-123.js",<br/><br/><br/>"controllers": "/assets/controllers/index-123.js"<br/><br/><br/>}<br/><br/><br/>}


    View Slide

  112. Indirect Names
    Use names rather than
    fi
    les
    {<br/><br/><br/>"imports": {<br/><br/><br/>"application": "/assets/application-123.js",<br/><br/><br/>"@hotwired/turbo-rails": "/assets/turbo.min-123.js",<br/><br/><br/>"@hotwired/stimulus": "/assets/stimulus.min-123.js",<br/><br/><br/>"@hotwired/stimulus-loading": "/assets/stimulus-loading-123.js",<br/><br/><br/>"controllers/application": "/assets/controllers/application-123.js",<br/><br/><br/>"controllers/hello_controller": "/assets/controllers/hello_controller-123.js",<br/><br/><br/>"controllers": "/assets/controllers/index-123.js"<br/><br/><br/>}<br/><br/><br/>}


    View Slide

  113. We're Not Bundling JS

    View Slide

  114. Better Caching

    View Slide

  115. More Requests

    View Slide

  116. HTTP2: Concurrent Requests on
    a Single Socket

    View Slide

  117. Rack

    View Slide

  118. Rack Defines an Interface
    Easily change webservers
    Rack Application
    Webserver
    Falcon

    View Slide

  119. Rack Interface
    rack_app = lambda do |env|


    [200, {}, ["neat"]]


    end


    def webserver(app)


    status, headers, body = app.call({})


    $socket.puts status


    $socket.puts headers


    body.each do |chunk|


    $socket.puts chunk


    end


    end


    webserver(rack_app)
    Rack App
    Webserver
    Request


    Data
    Response


    Data

    View Slide

  120. Rack + HTTP

    View Slide

  121. Rack + TCP Connection

    View Slide

  122. HTTP Timeline + First Commit of Rack
    Starting with HTTP/0.9
    1990
    1998
    2006
    2014
    2022
    Websockets
    2011
    HTTP/3
    2020
    HTTP/2
    2015
    Rack
    2007
    HTTP/1.1
    1997
    HTTP/1.0
    1996
    HTTP/0.9
    1991

    View Slide

  123. HTTP 0.9 Request / Response
    Connections were closed
    Client New TCP Connection Server
    HTTP Request
    HTTP Response
    perl ...
    ENV
    rack_app = lambda do |env|


    [200, {}, ["neat"]]


    end

    View Slide

  124. HTTP/1.0
    RFC 1945
    1990
    1998
    2006
    2014
    2022
    Websockets
    2011
    HTTP/3
    2020
    HTTP/2
    2015
    Rack
    2007
    HTTP/1.1
    1997
    HTTP/1.0
    1996
    HTTP/0.9
    1991

    View Slide

  125. Connections are Expensive

    View Slide

  126. Serial Requests

    View Slide

  127. Fetch assets in parallel?












    HELLO!


    Happy Friday!!!






    View Slide

  128. HTTP/1.1
    RFC 2068
    1990
    1998
    2006
    2014
    2022
    Websockets
    2011
    HTTP/3
    2020
    HTTP/2
    2015
    Rack
    2007
    HTTP/1.1
    1997
    HTTP/1.0
    1996
    HTTP/0.9
    1991

    View Slide

  129. HTTP/1.1: Keep Alive

    View Slide

  130. HTTP 1.1 Request / Response
    Connections are kept open
    Client New TCP Connection Server
    HTTP Request
    HTTP Response
    ruby ...
    ruby ...
    HTTP Request
    HTTP Response

    View Slide

  131. HTTP/1.1: Have 2 Connections

    View Slide

  132. HTTP 1.1 Multiple Connections
    Thanks buddy!
    New TCP Connection Server
    HTTP Request
    HTTP Response
    ruby ...
    ruby ...
    HTTP Request
    HTTP Response
    Client
    New TCP Connection Server
    HTTP Request
    HTTP Response
    ruby ...
    ruby ...
    HTTP Request
    HTTP Response

    View Slide

  133. Requests Are Still Serial

    View Slide

  134. Bundling Assets

    View Slide

  135. Rack is released!
    Version 0.1
    1990
    1998
    2006
    2014
    2022
    Websockets
    2011
    HTTP/3
    2020
    HTTP/2
    2015
    Rack
    2007
    HTTP/1.1
    1997
    HTTP/1.0
    1996
    HTTP/0.9
    1991

    View Slide

  136. Based on PEP 333

    View Slide

  137. "HTTP Translation"

    View Slide

  138. Environment Variables -> Hash

    View Slide

  139. stdout -> [200, {}, ["hi"]]

    View Slide

  140. Full Rack Application Example
    rack_app = lambda do |env|


    [200, # Status Code


    { "X-Cool-Header" => "Neat" }, # Headers


    ["Response Body"]] # Response Body


    end

    View Slide

  141. Websockets
    Sockets, but for the web!
    1990
    1998
    2006
    2014
    2022
    Websockets
    2011
    HTTP/3
    2020
    HTTP/2
    2015
    Rack
    2007
    HTTP/1.1
    1997
    HTTP/1.0
    1996
    HTTP/0.9
    1991

    View Slide

  142. Bidirectional Communication

    View Slide

  143. Websockets: NOT HTTP

    View Slide

  144. 2013: Rack 1.5

    View Slide

  145. Hijacking: Slap a proc in there!
    Get access to the socket
    rack_app = lambda do |env|


    io = env["rack.hijack"].call


    io.write "neat"


    io.close # socket is closed


    # Return stuff?


    [200, {}, []]


    end

    View Slide

  146. HTTP Translator +


    Socket Getter? 🤷

    View Slide

  147. HTTP/2
    It's 0.9 better than HTTP 1.1
    1990
    1998
    2006
    2014
    2022
    Websockets
    2011
    HTTP/3
    2020
    HTTP/2
    2015
    Rack
    2007
    HTTP/1.1
    1997
    HTTP/1.0
    1996
    HTTP/0.9
    1991

    View Slide

  148. SSL Encouraged

    View Slide

  149. Compressed Headers

    View Slide

  150. Binary Protocol

    View Slide

  151. Request / Response Multiplexing
    Concurrent requests on the same socket
    Client New SSL TCP Connection Server
    H2 Request 1
    H2 Response 2
    ruby ...
    ruby ...
    H2 Request 2
    H2 Response 1

    View Slide

  152. Import Maps

    View Slide

  153. Rails 7:


    We want you to use H2?

    View Slide

  154. How?

    View Slide

  155. https://github.com/socketry/falcon

    View Slide

  156. HTTP2 Proxy Server

    View Slide

  157. HTTP 2 Proxy Server
    Proxy translates to HTTP 1.1
    H2O Unicorn Rails
    HTTP 1.1 Rack
    HTTP 2

    View Slide

  158. Push Promise

    View Slide

  159. "Take this resource"

    View Slide

  160. What if the client already has the
    resource cached? 😅

    View Slide

  161. Push Promise in HTTP 1.1

    View Slide

  162. HTTP Status Code 103:


    Early Hints

    View Slide

  163. Sending Server Push with H2 Proxy
    HTTP 1.1 server sends 103, proxy converts to PUSH
    HTTP/1.1 103 Early Hints


    Link: ; rel=preload; as=style


    Link: ; rel=preload; as=script


    HTTP/1.1 200 OK


    Date: Fri, 26 May 2022 10:02:11 GMT


    Content-Length: 1234


    Content-Type: text/html; charset=utf-8


    Link: ; rel=preload; as=style


    Link: ; rel=preload; as=script
    HTTP 1.1 Response

    View Slide

  164. Sending Server Push with H2 Proxy
    HTTP 1.1 server sends 103, proxy converts to PUSH
    HTTP/1.1 103 Early Hints


    Link: ; rel=preload; as=style


    Link: ; rel=preload; as=script
    HTTP 103 Response
    H2O Unicorn
    HTTP 1.1
    HTTP 2
    PUSH /style.css
    PUSH /script.js

    View Slide

  165. Rails + (Puma || Falcon)

    View Slide

  166. Slap another lambda in the env hash!
    Great!
    class Server


    def call env


    env['rack.early_hints'].call([


    { link: "/style.css", as: "style" }, # push /style.css


    { link: "/script.js" }]) # push /script.js


    [200, { "X-Hello" => "World" }, ["Hello world!"]]


    end


    end

    View Slide

  167. 2020: HTTP/3

    View Slide

  168. Rails 7 is HTTP/2 by default

    View Slide

  169. Do we need H2 servers?

    View Slide

  170. What does H2 mean for Hijack?

    View Slide

  171. Slapping lambdas in a hash
    seems unsustainable

    View Slide

  172. Next Generation Rack

    View Slide

  173. Easy to Use

    View Slide

  174. Easy to Extend

    View Slide

  175. Easy to Deploy

    View Slide

  176. This Whole Thing Is Moot

    View Slide

  177. Ruby 3.1: WASM support

    View Slide

  178. https://ruby-syntax-tree.github.io

    View Slide

  179. Next Year:


    Rails in the browser,


    React on the server

    View Slide

  180. THANKS
    PORTLAND!
    See you next year! 😘

    View Slide