Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
What is Rack
Hijacking API
2016-12-03 at rubyconf.tw
1
Who am I?
• Kiyoshi Nomo
• @kysnm
• Web Application Engineer
• Goodpatch, Inc.
http://goodpatch.com/
https://prottapp.com/...
Agenda
• The Basics
• About the SPEC
• About the implementation
• Take a quick look at ActionCable
• Conclusion
3
The Basics
4
Who made this API?
5
6
Why it was made?
• Rack didn't have an API that allows
for IO-like streaming.
• for WebSocket
• for HTTP2
https://github.c...
Similar implementation
• Golang's Hijacker interface.
• Probably, This API would made
based on this interface.
https://git...
Support Servers
• puma
• passenger
• thin
• webrick (only partial hijack is
supported.)
• etc…
9
About the SPEC
10
Two mode of Hijaking
• Full hijacking
• Partial hijacking
http://www.rubydoc.info/github/
rack/rack/master/file/
SPEC#Hija...
The timing of Full
hijacking
• Request (before status)
12
The conditions of Full
hijacking
env['rack.hijack?'] == true
env['rack.hijack'].respond_to?(:call) == true
env['rack.hijac...
Your responsibility of
Full hijacking
• Outputting any HTTP headers, if
applicable.
• Closing the IO object when you no
lo...
class HijackWrapper
include Assertion
extend Forwardable
REQUIRED_METHODS = [
:read, :write, :read_nonblock, :write_nonblo...
def check_hijack(env)
if env[RACK_IS_HIJACK]
original_hijack = env[RACK_HIJACK]
assert("rack.hijack must respond to call")...
The timing of Partial
hijacking
• Response (after headers)
17
The conditions of
Partial hijacking
• an application may set the special
header rack.hijack to an object
that responds to ...
Your responsibility of
Partial hijacking
• closing the socket when it’s no
longer needed.
19
def check_hijack_response(headers, env)
headers = Rack::Utils::HeaderHash.new(headers)
if env[RACK_IS_HIJACK] && headers[R...
About the
implementation
21
Introduce two servers
• rack (webrick)
• puma
22
Webrick (rack)
23
Webrick is
• supported only partial hijack.
24
How to configure?
• See the test/spec_webrick.rb
25
it "support Rack partial hijack" do
io_lambda = lambda{ |io|
5.times do
io.write "Davidrn"
end
io.close
}
@server.mount "/...
run lambda { |env|
io_lambda = lambda { |io|
i = 1
5.times do
io.write "Davidrn"
end
io.close
}
[
200,
[ [ 'rack.hijack', ...
Rack::Handler::Webrick::run
def self.run(app, options={})
environment = ENV['RACK_ENV'] || 'development'
default_host = en...
app
[1] pry(#<Rack::Handler::WEBrick>)> app
=> #<Rack::ContentLength:0x007fa0fa17f2a8
@app=
#<Rack::Chunked:0x007fa0fa17f2...
Webrick::HTTPServer#servic
e
si = servlet.get_instance(self,
*options)
@logger.debug(format("%s is
invoked.", si.class.nam...
Webrick::HTTPServlet::Abstr
actServlet::get_instance
def self.get_instance(server,
*options)
self.new(server, *options)
en...
Rack::Handler::Webrick#initi
alize
def initialize(server, app)
super server
@app = app
end
https://github.com/rack/rack/bl...
Rack::Handler::Webrick#serv
ice (Take out the io_lambda)
status, headers, body = @app.call(env)
begin
res.status = status....
Rack::Handler::Webrick#serv
ice (Calls the io_lambda)
if io_lambda
rd, wr = IO.pipe
res.body = rd
res.chunked = true
io_la...
response
<= Recv data, 35 bytes (0x23)
0000: David
0007: David
000e: David
0015: David
001c: David
== Info: transfer close...
puma
36
puma is
• threaded, cluster enabled server.
• supported two mode of hijacking.
37
Full hijacking example
run lambda { |env|
io = env['rack.hijack'].call
io.puts "HTTP/1.1 200rnr
nBLAH"
[-1, {}, []]
}
http...
Before
Puma::Runner#start_server
=> #0 start_server
<Puma::Runner#start_server()>
#1 [method] start_server
<Puma::Runner#s...
Puma::Runner#start_serv
er
def start_server
min_t = @options[:min_threads]
max_t = @options[:max_threads]
server = Puma::S...
app
[1] pry(#<Puma::Server>)> app
=> #<Puma::Configuration::ConfigMiddleware:0x007ffaf2badc50
@app=#<Proc:0x007ffaf2badfc0...
Puma::Single#run
begin
server.run.join
rescue Interrupt
# Swallow it
end
https://github.com/puma/puma/blob/
3.6.1/lib/puma...
Puma::Server#handle_ser
vers
if io = sock.accept_nonblock
client = Client.new io, @binder.env(sock)
if remote_addr_value
c...
Before
Puma::ThreadPool#spawn_thre
ad
=> #0 spawn_thread
<Puma::ThreadPool#spawn_thread()>
#1 [method] spawn_thread
<Puma:...
Puma::Server#run
(block)
process_client client, buffer
https://github.com/puma/puma/blob/
3.6.1/lib/puma/server.rb#L275
45
Puma::Server#process_cl
ient
while true
case handle_request(client, buffer)
when false
return
when :async
close_socket = f...
Puma::Server#handle_req
uest (arguments)
def handle_request(req, lines)
env = req.env
client = req.io
normalize_env env, r...
Puma::Server#handle_req
uest (HIJACK_P, HIJACK)
env[HIJACK_P] = true
env[HIJACK] = req
https://github.com/puma/puma/blob/
...
Puma::Client#call
# For the hijack protocol (allows us
to just put the Client object
# into the env)
def call
@hijacked = ...
Puma::Const
HIJACK_P = "rack.hijack?".freeze
HIJACK = "rack.hijack".freeze
HIJACK_IO =
"rack.hijack_io".freeze
https://git...
Puma::Server#handle_req
uest (@app.call)
begin
begin
status, headers, res_body =
@app.call(env)
return :async if req.hijac...
Partial hijacking
example
run lambda { |env|
body = lambda { |io| io.puts "BLAH
n"; io.close }
[200, { 'rack.hijack' => bo...
Puma::Server#handle_req
uest (@app.call)
begin
begin
status, headers, res_body =
@app.call(env)
return :async if req.hijac...
Puma::Server#handle_req
uest (response_hijack)
response_hijack = nil
headers.each do |k, vs|
case k.downcase
when CONTENT_...
Puma::Server#handle_reque
st (response_hijack.call)
if response_hijack
response_hijack.call client
return :async
end
https...
Take a quick look at
ActionCable
56
In
ActionCable::Connection::St
ream
57
ActionCable::Connection::
Stream#hijack_rack_socket
def hijack_rack_socket
return unless @socket_object.env['rack.hijack']...
ActionCable::Connection::
Stream#clean_rack_hijack
private
def clean_rack_hijack
return unless @rack_hijack_io
@event_loop...
Faye::RackStream#hijack
_rack_socket 1
def hijack_rack_socket
return unless
@socket_object.env['rack.hijack']
@socket_obje...
Faye::RackStream#hijack
_rack_socket 2
EventMachine.schedule do
begin
EventMachine.attach(@rack_hijack_io,
Reader) do |rea...
Faye::RackStream#hijack
_rack_socket 3
ensure
queue.push(nil)
end
end
queue.pop if
EventMachine.reactor_running?
end
https...
Faye::RackStream#clean_
rack_hijack
def clean_rack_hijack
return unless @rack_hijack_io
@rack_hijack_io_reader.close_conne...
Conclusion
64
Limitations
•I have not tried to spec out a full IO
API, and I'm not sure that we should.
•I have not tried to respec all ...
What?
this is a straw man that addresses this within
the confines of the rack 1.x spec. It's not an
attempt to build out w...
Thank you.
67
Reference
• http://www.rubydoc.info/github/rack/rack/
master/file/SPEC#Hijacking
• http://old.blog.phusion.nl/2013/01/23/th...
Upcoming SlideShare
Loading in …5
×

What is Rack Hijacking API

1,538 views

Published on

* The Basics
* About the SPEC
* About the implementation
* Take a quick look at ActionCable
* Conclusion

Published in: Engineering
  • Be the first to comment

What is Rack Hijacking API

  1. 1. What is Rack Hijacking API 2016-12-03 at rubyconf.tw 1
  2. 2. Who am I? • Kiyoshi Nomo • @kysnm • Web Application Engineer • Goodpatch, Inc. http://goodpatch.com/ https://prottapp.com/ 2
  3. 3. Agenda • The Basics • About the SPEC • About the implementation • Take a quick look at ActionCable • Conclusion 3
  4. 4. The Basics 4
  5. 5. Who made this API? 5
  6. 6. 6
  7. 7. Why it was made? • Rack didn't have an API that allows for IO-like streaming. • for WebSocket • for HTTP2 https://github.com/rack/rack/pull/ 481#issue-9702395 7
  8. 8. Similar implementation • Golang's Hijacker interface. • Probably, This API would made based on this interface. https://github.com/rack/rack/pull/ 481#issue-9702395 8
  9. 9. Support Servers • puma • passenger • thin • webrick (only partial hijack is supported.) • etc… 9
  10. 10. About the SPEC 10
  11. 11. Two mode of Hijaking • Full hijacking • Partial hijacking http://www.rubydoc.info/github/ rack/rack/master/file/ SPEC#Hijacking 11
  12. 12. The timing of Full hijacking • Request (before status) 12
  13. 13. The conditions of Full hijacking env['rack.hijack?'] == true env['rack.hijack'].respond_to?(:call) == true env['rack.hijack'].call must returns the io env['rack.hijack'].call is assigned the io to env['rack.hijack_io'] REQUIRED_METHOD = [:read, :write, :read_nonblock, :write_nonblock, :fl ush, :close, :close_read, :close_write, :closed?] REQUIRED_METHOD.all? { |m| env['rack.hijack_io'].respond_to?(m) } == true 13
  14. 14. Your responsibility of Full hijacking • Outputting any HTTP headers, if applicable. • Closing the IO object when you no longer need it. 14
  15. 15. class HijackWrapper include Assertion extend Forwardable REQUIRED_METHODS = [ :read, :write, :read_nonblock, :write_nonblock, :flush, :clos e, :close_read, :close_write, :closed? ] def_delegators :@io, *REQUIRED_METHODS def initialize(io) @io = io REQUIRED_METHODS.each do |meth| assert("rack.hijack_io must respond to #{meth}") { io.respond_to? meth } end end end https://github.com/rack/rack/blob/ fd1fbab1ec8c7fc49ac805aac47b1f12d4cc5a99/lib/rack/lint.rb#L494- L511 15
  16. 16. def check_hijack(env) if env[RACK_IS_HIJACK] original_hijack = env[RACK_HIJACK] assert("rack.hijack must respond to call") { original_hijack.respond_to?(:call) } env[RACK_HIJACK] = proc do io = original_hijack.call HijackWrapper.new(io) env[RACK_HIJACK_IO] = HijackWrapper.new(env[RACK_HIJACK_IO]) io end else assert("rack.hijack? is false, but rack.hijack is present") { env[RACK_HIJACK].nil? } assert("rack.hijack? is false, but rack.hijack_io is present") { env[RACK_HIJACK_IO].nil? } end end https://github.com/rack/rack/blob/ fd1fbab1ec8c7fc49ac805aac47b1f12d4cc5a99/lib/rack/ lint.rb#L513-L562 16
  17. 17. The timing of Partial hijacking • Response (after headers) 17
  18. 18. The conditions of Partial hijacking • an application may set the special header rack.hijack to an object that responds to #call accepting an argument that conforms to the rack.hijack_io protocol. 18
  19. 19. Your responsibility of Partial hijacking • closing the socket when it’s no longer needed. 19
  20. 20. def check_hijack_response(headers, env) headers = Rack::Utils::HeaderHash.new(headers) if env[RACK_IS_HIJACK] && headers[RACK_HIJACK] assert('rack.hijack header must respond to #call') { headers[RACK_HIJACK].respond_to? :call } original_hijack = headers[RACK_HIJACK] headers[RACK_HIJACK] = proc do |io| original_hijack.call HijackWrapper.new(io) end else assert('rack.hijack header must not be present if server does not support hijacking') { headers[RACK_HIJACK].nil? } end end https://github.com/rack/rack/blob/ fd1fbab1ec8c7fc49ac805aac47b1f12d4cc5a99/lib/rack/ lint.rb#L564-L614 20
  21. 21. About the implementation 21
  22. 22. Introduce two servers • rack (webrick) • puma 22
  23. 23. Webrick (rack) 23
  24. 24. Webrick is • supported only partial hijack. 24
  25. 25. How to configure? • See the test/spec_webrick.rb 25
  26. 26. it "support Rack partial hijack" do io_lambda = lambda{ |io| 5.times do io.write "Davidrn" end io.close } @server.mount "/partial", Rack::Handler::WEBrick, Rack::Lint.new(lambda{ |req| [ 200, [ [ "rack.hijack", io_lambda ] ], [""] ] }) Net::HTTP.start(@host, @port){ |http| res = http.get("/partial") res.body.must_equal "DavidrnDavidrnDavidrnDavidrnDavidrn" } end https://github.com/rack/rack/blob/ cabe6b33ca4601aa6acb56317ac1c819cf6dc4bb/test/spec_webrick.rb#L162-L183 26
  27. 27. run lambda { |env| io_lambda = lambda { |io| i = 1 5.times do io.write "Davidrn" end io.close } [ 200, [ [ 'rack.hijack', io_lambda ] ], [''] ] } 27
  28. 28. Rack::Handler::Webrick::run def self.run(app, options={}) environment = ENV['RACK_ENV'] || 'development' default_host = environment == 'development' ? 'localhost' : nil options[:BindAddress] = options.delete(:Host) || default_host options[:Port] ||= 8080 @server = ::WEBrick::HTTPServer.new(options) @server.mount "/", Rack::Handler::WEBrick, app yield @server if block_given? @server.start end https://github.com/rack/rack/blob/ cabe6b33ca4601aa6acb56317ac1c819cf6dc4bb/lib/rack/handler/ webrick.rb#L25-L35
  29. 29. app [1] pry(#<Rack::Handler::WEBrick>)> app => #<Rack::ContentLength:0x007fa0fa17f2a8 @app= #<Rack::Chunked:0x007fa0fa17f2f8 @app= #<Rack::CommonLogger:0x007fa0fa17f348 @app= #<Rack::ShowExceptions:0x007fa0fb208458 @app= #<Rack::Lint:0x007fa0fb2084a8 @app= #<Rack::TempfileReaper:0x007fa0fb208520 @app=#<Proc:0x007fa0fb368c08@/tmp/rack_hijack_test/ webrick/config.ru:1 (lambda)>>, @content_length=nil>>, @logger=#<IO:<STDERR>>>>>
  30. 30. Webrick::HTTPServer#servic e si = servlet.get_instance(self, *options) @logger.debug(format("%s is invoked.", si.class.name)) si.service(req, res) https://github.com/ruby/ruby/blob/ v2_3_3/lib/webrick/ httpserver.rb#L138-L140
  31. 31. Webrick::HTTPServlet::Abstr actServlet::get_instance def self.get_instance(server, *options) self.new(server, *options) end https://github.com/ruby/ruby/blob/ v2_3_3/lib/webrick/httpservlet/ abstract.rb#L85-L87
  32. 32. Rack::Handler::Webrick#initi alize def initialize(server, app) super server @app = app end https://github.com/rack/rack/blob/ cabe6b33ca4601aa6acb56317ac1c819cf6 dc4bb/lib/rack/handler/ webrick.rb#L52-L55
  33. 33. Rack::Handler::Webrick#serv ice (Take out the io_lambda) status, headers, body = @app.call(env) begin res.status = status.to_i io_lambda = nil headers.each { |k, vs| if k == RACK_HIJACK io_lambda = vs elsif k.downcase == "set-cookie" res.cookies.concat vs.split("n") else # Since WEBrick won't accept repeated headers, # merge the values per RFC 1945 section 4.2. res[k] = vs.split("n").join(", ") end } https://github.com/rack/rack/blob/cabe6b33ca4601aa6acb56317ac1c819cf6dc4bb/ lib/rack/handler/webrick.rb#L86-L100
  34. 34. Rack::Handler::Webrick#serv ice (Calls the io_lambda) if io_lambda rd, wr = IO.pipe res.body = rd res.chunked = true io_lambda.call wr elsif body.respond_to?(:to_path) res.body = ::File.open(body.to_path, 'rb') else body.each { |part| res.body << part } end ensure body.close if body.respond_to? :close end https://github.com/rack/rack/blob/cabe6b33ca4601aa6acb56317ac1c819cf6dc4bb/ lib/rack/handler/webrick.rb#L86-L100
  35. 35. response <= Recv data, 35 bytes (0x23) 0000: David 0007: David 000e: David 0015: David 001c: David == Info: transfer closed with outstanding read data remaining == Info: Curl_http_done: called premature == 1 == Info: Closing connection 0 https://gist.github.com/kysnm/ ca5237d4ac96764b9cfe6ac1547710cf
  36. 36. puma 36
  37. 37. puma is • threaded, cluster enabled server. • supported two mode of hijacking. 37
  38. 38. Full hijacking example run lambda { |env| io = env['rack.hijack'].call io.puts "HTTP/1.1 200rnr nBLAH" [-1, {}, []] } https://github.com/puma/puma/blob/ 3.6.1/test/hijack.ru 38
  39. 39. Before Puma::Runner#start_server => #0 start_server <Puma::Runner#start_server()> #1 [method] start_server <Puma::Runner#start_server()> #2 [method] run <Puma::Single#run()> #3 [method] run <Puma::Launcher#run()> #4 [method] run <Puma::CLI#run()> 39
  40. 40. Puma::Runner#start_serv er def start_server min_t = @options[:min_threads] max_t = @options[:max_threads] server = Puma::Server.new app, @launcher.events, @options server.min_threads = min_t server.max_threads = max_t server.inherit_binder @launcher.binder if @options[:mode] == :tcp server.tcp_mode! end unless development? server.leak_stack_on_error = false end server end https://github.com/puma/puma/blob/3.6.1/lib/puma/runner.rb#L140-L160 40
  41. 41. app [1] pry(#<Puma::Server>)> app => #<Puma::Configuration::ConfigMiddleware:0x007ffaf2badc50 @app=#<Proc:0x007ffaf2badfc0@puma/hijack.ru:1 (lambda)>, @config= #<Puma::Configuration:0x007ffaf2c75110 @options= #<Puma::LeveledOptions:0x007ffaf2c74f08 @cur={}, @defaults= {:min_threads=>0, :max_threads=>16, :log_requests=>false, :debug=>false, :binds=>["tcp://0.0.0.0:9292"], :workers=>0, … snip … 41
  42. 42. Puma::Single#run begin server.run.join rescue Interrupt # Swallow it end https://github.com/puma/puma/blob/ 3.6.1/lib/puma/single.rb#L103-L107 42
  43. 43. Puma::Server#handle_ser vers if io = sock.accept_nonblock client = Client.new io, @binder.env(sock) if remote_addr_value client.peerip = remote_addr_value elsif remote_addr_header client.remote_addr_header = remote_addr_header end pool << client pool.wait_until_not_full unless queue_requests end https://github.com/puma/puma/blob/3.6.1/lib/puma/ server.rb#L333-L343 43
  44. 44. Before Puma::ThreadPool#spawn_thre ad => #0 spawn_thread <Puma::ThreadPool#spawn_thread()> #1 [method] spawn_thread <Puma::ThreadPool#spawn_thread()> #2 [block] block in << <Puma::ThreadPool#<<(work)> #3 [method] << <Puma::ThreadPool#<<(work)> #4 [block] block in handle_servers <Puma::Server#handle_servers()> #5 [method] handle_servers <Puma::Server#handle_servers()> #6 [block] block in run <Puma::Server#run(background=?)> 44
  45. 45. Puma::Server#run (block) process_client client, buffer https://github.com/puma/puma/blob/ 3.6.1/lib/puma/server.rb#L275 45
  46. 46. Puma::Server#process_cl ient while true case handle_request(client, buffer) when false return when :async close_socket = false return when true return unless @queue_requests buffer.reset https://github.com/puma/puma/blob/3.6.1/lib/ puma/server.rb#L275 46
  47. 47. Puma::Server#handle_req uest (arguments) def handle_request(req, lines) env = req.env client = req.io normalize_env env, req env[PUMA_SOCKET] = client https://github.com/puma/puma/blob/ 3.6.1/lib/puma/server.rb#L549-L555 47
  48. 48. Puma::Server#handle_req uest (HIJACK_P, HIJACK) env[HIJACK_P] = true env[HIJACK] = req https://github.com/puma/puma/blob/ 3.6.1/lib/puma/server.rb#L561-L562 48
  49. 49. Puma::Client#call # For the hijack protocol (allows us to just put the Client object # into the env) def call @hijacked = true env[HIJACK_IO] ||= @io end https://github.com/puma/puma/blob/ 3.6.1/lib/puma/client.rb#L69-L74 49
  50. 50. Puma::Const HIJACK_P = "rack.hijack?".freeze HIJACK = "rack.hijack".freeze HIJACK_IO = "rack.hijack_io".freeze https://github.com/puma/puma/blob/ 3.6.1/lib/puma/const.rb#L249-L251 50
  51. 51. Puma::Server#handle_req uest (@app.call) begin begin status, headers, res_body = @app.call(env) return :async if req.hijacked https://github.com/puma/puma/blob/ 3.6.1/lib/puma/server.rb#L576-L580 51
  52. 52. Partial hijacking example run lambda { |env| body = lambda { |io| io.puts "BLAH n"; io.close } [200, { 'rack.hijack' => body }, []] } https://github.com/puma/puma/blob/ 3.6.1/test/hijack2.ru 52
  53. 53. Puma::Server#handle_req uest (@app.call) begin begin status, headers, res_body = @app.call(env) return :async if req.hijacked https://github.com/puma/puma/blob/ 3.6.1/lib/puma/server.rb#L576-L580 53
  54. 54. Puma::Server#handle_req uest (response_hijack) response_hijack = nil headers.each do |k, vs| case k.downcase when CONTENT_LENGTH2 content_length = vs next when TRANSFER_ENCODING allow_chunked = false content_length = nil when HIJACK response_hijack = vs next end https://github.com/puma/puma/blob/3.6.1/lib/puma/server.rb#L653- L666 54
  55. 55. Puma::Server#handle_reque st (response_hijack.call) if response_hijack response_hijack.call client return :async end https://github.com/puma/puma/blob/ 3.6.1/lib/puma/server.rb#L705-L708 55
  56. 56. Take a quick look at ActionCable 56
  57. 57. In ActionCable::Connection::St ream 57
  58. 58. ActionCable::Connection:: Stream#hijack_rack_socket def hijack_rack_socket return unless @socket_object.env['rack.hijack'] @socket_object.env['rack.hijack'].call @rack_hijack_io = @socket_object.env['rack.hijack_io'] @event_loop.attach(@rack_hijack_io, self) end https://github.com/rails/rails/blob/v5.0.0.1/ actioncable/lib/action_cable/connection/ stream.rb#L40-L47 58
  59. 59. ActionCable::Connection:: Stream#clean_rack_hijack private def clean_rack_hijack return unless @rack_hijack_io @event_loop.detach(@rack_hijack_io, self) @rack_hijack_io = nil end https://github.com/rails/rails/blob/ v5.0.0.1/actioncable/lib/action_cable/ connection/stream.rb#L40-L47 59
  60. 60. Faye::RackStream#hijack _rack_socket 1 def hijack_rack_socket return unless @socket_object.env['rack.hijack'] @socket_object.env['rack.hijack'].call @rack_hijack_io = @socket_object.env['rack.hijack_io'] queue = Queue.new https://github.com/faye/faye-websocket- ruby/blob/0.10.5/lib/faye/ rack_stream.rb#L30-L36 60
  61. 61. Faye::RackStream#hijack _rack_socket 2 EventMachine.schedule do begin EventMachine.attach(@rack_hijack_io, Reader) do |reader| reader.stream = self if @rack_hijack_io @rack_hijack_io_reader = reader else reader.close_connection_after_writing end https://github.com/faye/faye-websocket-ruby/ blob/0.10.5/lib/faye/rack_stream.rb#L37-L46 61
  62. 62. Faye::RackStream#hijack _rack_socket 3 ensure queue.push(nil) end end queue.pop if EventMachine.reactor_running? end https://github.com/faye/faye-websocket- ruby/blob/0.10.5/lib/faye/ rack_stream.rb#L47-L53 62
  63. 63. Faye::RackStream#clean_ rack_hijack def clean_rack_hijack return unless @rack_hijack_io @rack_hijack_io_reader.close_connection_afte r_writing @rack_hijack_io = @rack_hijack_io_reader = nil end https://github.com/faye/faye-websocket-ruby/ blob/0.10.5/lib/faye/rack_stream.rb#L55-L59 63
  64. 64. Conclusion 64
  65. 65. Limitations •I have not tried to spec out a full IO API, and I'm not sure that we should. •I have not tried to respec all of the HTTP / anti-HTTP semantics. •There is no spec for buffering or the like. •The intent is that this is an API to "get out the way”. https://github.com/rack/rack/pull/481 65
  66. 66. What? this is a straw man that addresses this within the confines of the rack 1.x spec. It's not an attempt to build out what I hope a 2.0 spec should be, but I am hoping that something like this will be enough to aid Rails 4s ventures, enable websockets, and a few other strategies. With HTTP2 around the corner, we'll likely want to revisit the IO API for 2.0, but we'll see how this plays out. Maybe IO wrapped around channels will be ok. https://github.com/rack/rack/pull/481 66
  67. 67. Thank you. 67
  68. 68. Reference • http://www.rubydoc.info/github/rack/rack/ master/file/SPEC#Hijacking • http://old.blog.phusion.nl/2013/01/23/the- new-rack-socket-hijacking-api/ • https://github.com/rack/rack/pull/481

×