diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/rainbows.rb | 8 | ||||
-rw-r--r-- | lib/rainbows/base.rb | 36 | ||||
-rw-r--r-- | lib/rainbows/const.rb | 1 | ||||
-rw-r--r-- | lib/rainbows/ev_core.rb | 2 | ||||
-rw-r--r-- | lib/rainbows/fiber/base.rb | 6 | ||||
-rw-r--r-- | lib/rainbows/fiber/rev.rb | 2 | ||||
-rw-r--r-- | lib/rainbows/http_response.rb | 41 | ||||
-rw-r--r-- | lib/rainbows/http_server.rb | 1 | ||||
-rw-r--r-- | lib/rainbows/rev/deferred_response.rb | 21 | ||||
-rw-r--r-- | lib/rainbows/revactor.rb | 2 |
10 files changed, 76 insertions, 44 deletions
diff --git a/lib/rainbows.rb b/lib/rainbows.rb index 5c8ee94..ccf211e 100644 --- a/lib/rainbows.rb +++ b/lib/rainbows.rb @@ -73,6 +73,14 @@ module Rainbows rv rescue Errno::EAGAIN, Errno::ECONNABORTED end + + # returns a string representing the address of the given client +io+ + # For local UNIX domain sockets, this will return a string referred + # to by the (non-frozen) Unicorn::HttpRequest::LOCALHOST constant. + def addr(io) + io.respond_to?(:peeraddr) ? + io.peeraddr.last : Unicorn::HttpRequest::LOCALHOST + end end # configures \Rainbows! with a given concurrency model to +use+ and diff --git a/lib/rainbows/base.rb b/lib/rainbows/base.rb index a29a5bb..27b4c1d 100644 --- a/lib/rainbows/base.rb +++ b/lib/rainbows/base.rb @@ -29,14 +29,35 @@ module Rainbows logger.info "Rainbows! #@use worker_connections=#@worker_connections" end + if IO.respond_to?(:copy_stream) + def write_body(client, body) + if body.respond_to?(:to_path) + io = body.respond_to?(:to_io) ? body.to_io : body.to_path + IO.copy_stream(io, client) + else + body.each { |chunk| client.write(chunk) } + end + ensure + body.respond_to?(:close) and body.close + end + else + def write_body(client, body) + body.each { |chunk| client.write(chunk) } + ensure + body.respond_to?(:close) and body.close + end + end + # once a client is accepted, it is processed in its entirety here # in 3 easy steps: read request, call app, write app response + # this is used by synchronous concurrency models + # Base, ThreadSpawn, ThreadPool def process_client(client) buf = client.readpartial(CHUNK_SIZE) # accept filters protect us here hp = HttpParser.new env = {} alive = true - remote_addr = TCPSocket === client ? client.peeraddr.last : LOCALHOST + remote_addr = Rainbows.addr(client) begin # loop while ! hp.headers(env, buf) @@ -49,17 +70,20 @@ module Rainbows HttpRequest::NULL_IO : Unicorn::TeeInput.new(client, env, hp, buf) env[REMOTE_ADDR] = remote_addr - response = app.call(env.update(RACK_DEFAULTS)) + status, headers, body = app.call(env.update(RACK_DEFAULTS)) - if 100 == response.first.to_i + if 100 == status client.write(EXPECT_100_RESPONSE) env.delete(HTTP_EXPECT) - response = app.call(env) + status, headers, body = app.call(env) end alive = hp.keepalive? && G.alive - out = [ alive ? CONN_ALIVE : CONN_CLOSE ] if hp.headers? - HttpResponse.write(client, response, out) + if hp.headers? + out = [ alive ? CONN_ALIVE : CONN_CLOSE ] + client.write(HttpResponse.header_string(status, headers, out)) + end + write_body(client, body) end while alive and hp.reset.nil? and env.clear # if we get any error, try to write something back to the client # assuming we haven't closed the socket, but don't get hung up diff --git a/lib/rainbows/const.rb b/lib/rainbows/const.rb index 99fb257..08c4821 100644 --- a/lib/rainbows/const.rb +++ b/lib/rainbows/const.rb @@ -17,7 +17,6 @@ module Rainbows CONN_CLOSE = "Connection: close\r\n" CONN_ALIVE = "Connection: keep-alive\r\n" - LOCALHOST = Unicorn::HttpRequest::LOCALHOST # client IO object that supports reading and writing directly # without filtering it through the HTTP chunk parser. diff --git a/lib/rainbows/ev_core.rb b/lib/rainbows/ev_core.rb index 3d02b8a..682bdd6 100644 --- a/lib/rainbows/ev_core.rb +++ b/lib/rainbows/ev_core.rb @@ -14,7 +14,7 @@ module Rainbows ASYNC_CLOSE = "async.close".freeze def post_init - @remote_addr = ::TCPSocket === @_io ? @_io.peeraddr.last : LOCALHOST + @remote_addr = Rainbows.addr(@_io) @env = {} @hp = HttpParser.new @state = :headers # [ :body [ :trailers ] ] :app_call :close diff --git a/lib/rainbows/fiber/base.rb b/lib/rainbows/fiber/base.rb index b731947..a056152 100644 --- a/lib/rainbows/fiber/base.rb +++ b/lib/rainbows/fiber/base.rb @@ -57,15 +57,17 @@ module Rainbows def schedule_sleepers max = nil now = Time.now + fibs = [] ZZ.delete_if { |fib, time| if now >= time - fib.resume + fibs << fib now = Time.now else max = time false end } + fibs.each { |fib| fib.resume } max.nil? || max > (now + 1) ? 1 : max - now end @@ -76,7 +78,7 @@ module Rainbows hp = HttpParser.new env = {} alive = true - remote_addr = TCPSocket === io ? io.peeraddr.last : LOCALHOST + remote_addr = Rainbows.addr(io) begin # loop while ! hp.headers(env, buf) diff --git a/lib/rainbows/fiber/rev.rb b/lib/rainbows/fiber/rev.rb index bd9638f..a733103 100644 --- a/lib/rainbows/fiber/rev.rb +++ b/lib/rainbows/fiber/rev.rb @@ -80,7 +80,7 @@ module Rainbows::Fiber hp = HttpParser.new env = {} alive = true - remote_addr = TCPSocket === io ? io.peeraddr.last : LOCALHOST + remote_addr = Rainbows.addr(io) begin # loop buf << (client.read_timeout or return) until hp.headers(env, buf) diff --git a/lib/rainbows/http_response.rb b/lib/rainbows/http_response.rb index 55c2ad2..1933218 100644 --- a/lib/rainbows/http_response.rb +++ b/lib/rainbows/http_response.rb @@ -1,34 +1,35 @@ # -*- encoding: binary -*- require 'time' -require 'rainbows' module Rainbows class HttpResponse < ::Unicorn::HttpResponse - def self.write(socket, rack_response, out = []) - status, headers, body = rack_response - - if Array === out - status = CODES[status.to_i] || status + def self.header_string(status, headers, out) + status = CODES[status.to_i] || status - headers.each do |key, value| - next if %r{\AX-Rainbows-}i =~ key - next if SKIP.include?(key.downcase) - if value =~ /\n/ - # avoiding blank, key-only cookies with /\n+/ - out.concat(value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }) - else - out << "#{key}: #{value}\r\n" - end + headers.each do |key, value| + next if %r{\AX-Rainbows-}i =~ key + next if SKIP.include?(key.downcase) + if value =~ /\n/ + # avoiding blank, key-only cookies with /\n+/ + out.concat(value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }) + else + out << "#{key}: #{value}\r\n" end - - socket.write("HTTP/1.1 #{status}\r\n" \ - "Date: #{Time.now.httpdate}\r\n" \ - "Status: #{status}\r\n" \ - "#{out.join('')}\r\n") end + "HTTP/1.1 #{status}\r\n" \ + "Date: #{Time.now.httpdate}\r\n" \ + "Status: #{status}\r\n" \ + "#{out.join('')}\r\n" + end + + def self.write(socket, rack_response, out = []) + status, headers, body = rack_response + out.instance_of?(Array) and + socket.write(header_string(status, headers, out)) + body.each { |chunk| socket.write(chunk) } ensure body.respond_to?(:close) and body.close diff --git a/lib/rainbows/http_server.rb b/lib/rainbows/http_server.rb index c4f804a..ea2e23f 100644 --- a/lib/rainbows/http_server.rb +++ b/lib/rainbows/http_server.rb @@ -1,5 +1,4 @@ # -*- encoding: binary -*- -require 'rainbows' module Rainbows class HttpServer < ::Unicorn::HttpServer diff --git a/lib/rainbows/rev/deferred_response.rb b/lib/rainbows/rev/deferred_response.rb index b69c7be..dd7a229 100644 --- a/lib/rainbows/rev/deferred_response.rb +++ b/lib/rainbows/rev/deferred_response.rb @@ -10,9 +10,13 @@ module Rainbows G = Rainbows::G HH = Rack::Utils::HeaderHash - def self.defer!(client, response, out) - body = response.last - headers = HH.new(response[1]) + def self.write(client, response, out) + status, headers, body = response + + body.respond_to?(:to_path) or + return HttpResponse.write(client, response, out) + + headers = HH.new(headers) # to_io is not part of the Rack spec, but make an exception # here since we can't get here without checking to_path first @@ -39,16 +43,11 @@ module Rainbows headers.delete('Transfer-Encoding') headers['Content-Length'] ||= st.size.to_s else # char/block device, directory, whatever... nobody cares - return response + return HttpResponse.write(client, response, out) end client.defer_body(io, out) - [ response.first, headers.to_hash, [] ] - end - - def self.write(client, response, out) - response.last.respond_to?(:to_path) and - response = defer!(client, response, out) - HttpResponse.write(client, response, out) + out.nil? or + client.write(HttpResponse.header_string(status, headers.to_hash, out)) end def initialize(io, client, do_chunk, body) diff --git a/lib/rainbows/revactor.rb b/lib/rainbows/revactor.rb index ab65184..ed08f2c 100644 --- a/lib/rainbows/revactor.rb +++ b/lib/rainbows/revactor.rb @@ -37,7 +37,7 @@ module Rainbows rd_args << RD_ARGS client.remote_addr else - LOCALHOST + Unicorn::HttpRequest::LOCALHOST end buf = client.read(*rd_args) hp = HttpParser.new |