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. RailsConf 2022
    It's been a minute!

    View full-size slide

  2. 🇺🇦🇺🇦🇺🇦

    View full-size slide

  3. RIP Little Buddy 😭

    View full-size slide

  4. ~5 Photos / Day 🤣

    View full-size slide

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

    View full-size slide

  6. Tenderlove's Cool Stuff

    View full-size slide

  7. ˑˑˑˑˑ


    0 Stars: Do Not Recommend

    View full-size slide

  8. JIT: Generate Machine Code at
    Runtime

    View full-size slide

  9. Machine Code:


    Sequence of Bytes

    View full-size slide

  10. Ruby:


    Can Make a Sequence of Bytes

    View full-size slide

  11. 🤔 Pure Ruby JIT? 🤔

    View full-size slide

  12. Yes! Yes, we can.

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  16. Professional Software
    Development

    View full-size slide

  17. Apple M1: AArch64

    View full-size slide

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

    View full-size slide

  19. 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 full-size slide

  20. 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 full-size slide

  21. Not Amateur Enough

    View full-size slide

  22. Extremely Amateur

    View full-size slide

  23. 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 full-size slide

  24. 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 full-size slide

  25. JITBuffer:


    Executable Memory

    View full-size slide

  26. Linux / macOS

    View full-size slide

  27. Any Platform

    View full-size slide

  28. 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 full-size slide

  29. Test on x86_64 and ARM

    View full-size slide

  30. 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 full-size slide

  31. 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 full-size slide

  32. If Statements Mean "Complexity"

    View full-size slide

  33. Can we ask the CPU itself?

    View full-size slide

  34. Machine Code is Just Bytes

    View full-size slide

  35. Bytes Valid for


    x86_64 and ARM64?

    View full-size slide

  36. Worst Best Idea Ever!

    View full-size slide

  37. 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 full-size slide

  38. 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 full-size slide

  39. 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 full-size slide

  40. 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 full-size slide

  41. 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 full-size slide

  42. 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 full-size slide

  43. 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 full-size slide

  44. 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 full-size slide

  45. 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 full-size slide

  46. Both Return 0x2B

    View full-size slide

  47. 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 full-size slide

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

    View full-size slide

  49. No If Statements!

    View full-size slide

  50. Complexity: 0

    View full-size slide

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

    View full-size slide

  52. Anatomy of a Tenderlove Talk

    View full-size slide

  53. 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 full-size slide

  54. 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 full-size slide

  55. New Features in Rails 7!!
    Rack

    View full-size slide

  56. Spring
    It is gone!

    View full-size slide

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


    • Hotwire Default


    • Turbo 7 Default


    • Stimulus 3 Default


    • JS Bundling


    • CSS Bundling

    View full-size slide

  58. I am not a
    Front End Developer

    View full-size slide

  59. No Idea
    what these words mean

    View full-size slide

  60. Import Maps and ES Modules

    View full-size slide

  61. Sprockets
    Webpacker
    ESM + Import Maps

    View full-size slide

  62. 🫠🫠🫠🫠
    "yay"

    View full-size slide

  63. 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 full-size slide

  64. 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 full-size slide

  65. 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 full-size slide

  66. Pinning Libraries

    View full-size slide

  67. 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 full-size slide

  68. 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 full-size slide

  69. 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 full-size slide

  70. 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 full-size slide

  71. 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 full-size slide

  72. 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 full-size slide

  73. 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 full-size slide

  74. 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 full-size slide

  75. 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 full-size slide

  76. We're Not Bundling JS

    View full-size slide

  77. Better Caching

    View full-size slide

  78. More Requests

    View full-size slide

  79. HTTP2: Concurrent Requests on
    a Single Socket

    View full-size slide

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

    View full-size slide

  81. 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 full-size slide

  82. Rack + TCP Connection

    View full-size slide

  83. 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 full-size slide

  84. 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 full-size slide

  85. 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 full-size slide

  86. Connections are Expensive

    View full-size slide

  87. Serial Requests

    View full-size slide

  88. Fetch assets in parallel?












    HELLO!


    Happy Friday!!!






    View full-size slide

  89. 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 full-size slide

  90. HTTP/1.1: Keep Alive

    View full-size slide

  91. 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 full-size slide

  92. HTTP/1.1: Have 2 Connections

    View full-size slide

  93. 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 full-size slide

  94. Requests Are Still Serial

    View full-size slide

  95. Bundling Assets

    View full-size slide

  96. 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 full-size slide

  97. Based on PEP 333

    View full-size slide

  98. "HTTP Translation"

    View full-size slide

  99. Environment Variables -> Hash

    View full-size slide

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

    View full-size slide

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


    [200, # Status Code


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


    ["Response Body"]] # Response Body


    end

    View full-size slide

  102. 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 full-size slide

  103. Bidirectional Communication

    View full-size slide

  104. Websockets: NOT HTTP

    View full-size slide

  105. 2013: Rack 1.5

    View full-size slide

  106. 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 full-size slide

  107. HTTP Translator +


    Socket Getter? 🤷

    View full-size slide

  108. 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 full-size slide

  109. SSL Encouraged

    View full-size slide

  110. Compressed Headers

    View full-size slide

  111. Binary Protocol

    View full-size slide

  112. 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 full-size slide

  113. Rails 7:


    We want you to use H2?

    View full-size slide

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

    View full-size slide

  115. HTTP2 Proxy Server

    View full-size slide

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

    View full-size slide

  117. Push Promise

    View full-size slide

  118. "Take this resource"

    View full-size slide

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

    View full-size slide

  120. Push Promise in HTTP 1.1

    View full-size slide

  121. HTTP Status Code 103:


    Early Hints

    View full-size slide

  122. 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 full-size slide

  123. 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 full-size slide

  124. Rails + (Puma || Falcon)

    View full-size slide

  125. 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 full-size slide

  126. 2020: HTTP/3

    View full-size slide

  127. Rails 7 is HTTP/2 by default

    View full-size slide

  128. Do we need H2 servers?

    View full-size slide

  129. What does H2 mean for Hijack?

    View full-size slide

  130. Slapping lambdas in a hash
    seems unsustainable

    View full-size slide

  131. Next Generation Rack

    View full-size slide

  132. Easy to Extend

    View full-size slide

  133. Easy to Deploy

    View full-size slide

  134. This Whole Thing Is Moot

    View full-size slide

  135. Ruby 3.1: WASM support

    View full-size slide

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

    View full-size slide

  137. Next Year:


    Rails in the browser,


    React on the server

    View full-size slide

  138. THANKS
    PORTLAND!
    See you next year! 😘

    View full-size slide