Module: Itsi::Server::RackInterface
- Included in:
- Itsi::Server
- Defined in:
- lib/itsi/server/rack_interface.rb
Class Method Summary collapse
-
.for(app) ⇒ Object
Builds a handler proc that is compatible with Rack applications.
Instance Method Summary collapse
-
#call(app, request) ⇒ Object
Interface to Rack applications.
-
#respond(request, status, headers, body) ⇒ Object
Itsi responses are asynchronous and can be streamed.
-
#streaming_body?(body) ⇒ Boolean
A streaming body is one that responds to #call and not #each.
Class Method Details
.for(app) ⇒ Object
Builds a handler proc that is compatible with Rack applications.
5 6 7 8 9 10 11 12 13 14 15 16 17 |
# File 'lib/itsi/server/rack_interface.rb', line 5 def self.for(app) require "rack" if app.is_a?(String) dir = File.(File.dirname(app)) Dir.chdir(dir) do loaded_app = ::Rack::Builder.parse_file(File.basename(app)) app = loaded_app.is_a?(Array) ? loaded_app.first : loaded_app end end lambda do |request| Server.respond(request, app.call(request.to_rack_env)) end end |
Instance Method Details
#call(app, request) ⇒ Object
Interface to Rack applications. Here we build the env, and invoke the Rack app’s call method. We then turn the Rack response into something Itsi server understands.
22 23 24 |
# File 'lib/itsi/server/rack_interface.rb', line 22 def call(app, request) respond request, app.call(request.to_rack_env) end |
#respond(request, status, headers, body) ⇒ Object
Itsi responses are asynchronous and can be streamed. Response chunks are sent using response.send_frame and the response is finished using response.close_write. If only a single chunk is written, you can use the #send_and_close method.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/itsi/server/rack_interface.rb', line 30 def respond(request, (status, headers, body)) response = request.response # Don't try and respond if we've been hijacked. # The hijacker is now responsible for this. return if request.hijacked # 1. Set Status response.status = status # 2. Set Headers body_streamer = streaming_body?(body) ? body : headers.delete("rack.hijack") headers.each do |key, value| if value.is_a?(Array) value.each do |v| response[key] = v end elsif value.is_a?(String) response[key] = value end end # 3. Set Body # As soon as we start setting the response # the server will begin to stream it to the client. # If we're partially hijacked or returned a streaming body, # stream this response. if body_streamer body_streamer.call(response) # If we're enumerable with more than one chunk # also stream, otherwise write in a single chunk elsif body.respond_to?(:each) || body.respond_to?(:to_ary) unless body.respond_to?(:each) body = body.to_ary raise "Body #to_ary didn't return an array" unless body.is_a?(Array) end # We offset this iteration intentionally, # to optimize for the case where there's only one chunk. buffer = nil body.each do |part| response << buffer.to_s if buffer buffer = part end response.send_and_close(buffer.to_s) else response.send_and_close(body.to_s) end ensure response.close_write body.close if body.respond_to?(:close) end |
#streaming_body?(body) ⇒ Boolean
A streaming body is one that responds to #call and not #each.
87 88 89 |
# File 'lib/itsi/server/rack_interface.rb', line 87 def streaming_body?(body) body.respond_to?(:call) && !body.respond_to?(:each) end |