Module: Rainbows::StreamResponseEpoll
- Defined in:
- lib/rainbows/stream_response_epoll.rb
Overview
Like Unicorn itself, this concurrency model is only intended for use behind nginx and completely unsupported otherwise. Even further from Unicorn, this isn’t even a good idea with normal LAN clients, only nginx!
It does NOT require a thread-safe Rack application at any point, but allows streaming data asynchronously via nginx (using the “X-Accel-Buffering: no” header to disable buffering).
Unlike Rainbows::Base, this does NOT support persistent connections or pipelining. All Rainbows! specific configuration options are ignored (except Rainbows::Configurator#use).
RubyGem Requirements
-
raindrops 0.6.0 or later
-
sleepy_penguin 3.0.1 or later
Instance Method Summary collapse
- #http_response_write(socket, status, headers, body) ⇒ Object
-
#process_client(client) ⇒ Object
once a client is accepted, it is processed in its entirety here in 3 easy steps: read request, call app, write app response.
Instance Method Details
#http_response_write(socket, status, headers, body) ⇒ Object
25 26 27 28 29 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 90 91 92 93 94 95 |
# File 'lib/rainbows/stream_response_epoll.rb', line 25 def http_response_write(socket, status, headers, body) hijack = ep_client = false if headers # don't set extra headers here, this is only intended for # consuming by nginx. code = status.to_i msg = Rack::Utils::HTTP_STATUS_CODES[code] buf = "HTTP/1.0 #{msg ? %Q(#{code} #{msg}) : status}\r\n" headers.each do |key, value| case key when "rack.hijack" hijack = value body = nil # ensure we do not close body else if /\n/ =~ value # avoiding blank, key-only cookies with /\n+/ value.split(/\n+/).each { |v| buf << "#{key}: #{v}\r\n" } else buf << "#{key}: #{value}\r\n" end end end buf << "X-Accel-Buffering: no\r\n\r\n".freeze case rv = socket.kgio_trywrite(buf) when nil then break when String # retry, socket buffer may grow buf = rv when :wait_writable ep_client = Client.new(socket, buf) if hijack ep_client.hijack(hijack) else body.each { |chunk| ep_client.write(chunk) } ep_client.close end # body is nil on hijack, in which case ep_client is never closed by us return end while true end if hijack hijack.call(socket) return end body.each do |chunk| if ep_client ep_client.write(chunk) else case rv = socket.kgio_trywrite(chunk) when nil then break when String # retry, socket buffer may grow chunk = rv when :wait_writable ep_client = Client.new(socket, chunk) break end while true end end ensure return if hijack body.respond_to?(:close) and body.close if ep_client ep_client.close else socket.shutdown socket.close end end |
#process_client(client) ⇒ Object
once a client is accepted, it is processed in its entirety here in 3 easy steps: read request, call app, write app response
99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/rainbows/stream_response_epoll.rb', line 99 def process_client(client) status, headers, body = @app.call(env = @request.read(client)) if 100 == status.to_i client.write("HTTP/1.1 100 Continue\r\n\r\n".freeze) env.delete('HTTP_EXPECT'.freeze) status, headers, body = @app.call(env) end @request.headers? or headers = nil return if @request.hijacked? http_response_write(client, status, headers, body) rescue => e handle_error(client, e) end |