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, env) ⇒ 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(env = request.to_rack_env), 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(env = request.to_rack_env), env end |
#respond(request, status, headers, body, env) ⇒ 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 85 86 87 88 89 |
# File 'lib/itsi/server/rack_interface.rb', line 30 def respond(request, (status, headers, body), env) 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") response.reserve_headers(headers.size) for key, value in headers case value when String then response[key] = value when Array value.each do |v| response[key] = v end end end # 3. Set Body # As soon as we start setting the response # the server will begin to stream it to the client. if body_streamer # If we're partially hijacked or returned a streaming body, # stream this response. body_streamer.call(response) elsif body.respond_to?(:each) || body.respond_to?(:to_ary) # If we're enumerable with more than one chunk # also stream, otherwise write in a single chunk 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 rescue EOFError response.close ensure RackEnvPool.checkin(env) body.close if body.respond_to?(:close) end |
#streaming_body?(body) ⇒ Boolean
A streaming body is one that responds to #call and not #each.
92 93 94 |
# File 'lib/itsi/server/rack_interface.rb', line 92 def streaming_body?(body) body.respond_to?(:call) && !body.respond_to?(:each) end |