about summary refs log tree commit homepage
path: root/lib/rainbows
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rainbows')
-rw-r--r--lib/rainbows/base.rb36
-rw-r--r--lib/rainbows/const.rb1
-rw-r--r--lib/rainbows/ev_core.rb2
-rw-r--r--lib/rainbows/fiber/base.rb6
-rw-r--r--lib/rainbows/fiber/rev.rb2
-rw-r--r--lib/rainbows/http_response.rb41
-rw-r--r--lib/rainbows/http_server.rb1
-rw-r--r--lib/rainbows/rev/deferred_response.rb21
-rw-r--r--lib/rainbows/revactor.rb2
9 files changed, 68 insertions, 44 deletions
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