Module: Itsi::Server::RackInterface
- Included in:
- Itsi::Server
- Defined in:
- lib/itsi/server/rack_interface.rb
Defined Under Namespace
Classes: PartialHijackStream
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.
44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/itsi/server/rack_interface.rb', line 44 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.
61 62 63 |
# File 'lib/itsi/server/rack_interface.rb', line 61 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.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/itsi/server/rack_interface.rb', line 69 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 hijack_callback = headers.delete("rack.hijack") body_streamer = streaming_body?(body) ? body : hijack_callback 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 hijack_callback stream = status == 101 ? request.partial_hijack : PartialHijackStream.new(response) body_streamer.call(stream) elsif body_streamer # If we're partially hijacked or returned a streaming body, # stream this response. body_streamer.call(response) elsif body.is_a?(Array) if body.length == 1 response.send_and_close(body[0].to_s) else buffer = nil body.each do |part| response << buffer.to_s if buffer buffer = part end response.send_and_close(buffer.to_s) end 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.
147 148 149 |
# File 'lib/itsi/server/rack_interface.rb', line 147 def streaming_body?(body) body.respond_to?(:call) && !body.respond_to?(:each) end |