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.rb51
-rw-r--r--lib/rainbows/fiber/base.rb29
2 files changed, 66 insertions, 14 deletions
diff --git a/lib/rainbows/base.rb b/lib/rainbows/base.rb
index 2627719..24924cb 100644
--- a/lib/rainbows/base.rb
+++ b/lib/rainbows/base.rb
@@ -39,25 +39,56 @@ module Rainbows::Base
     logger.info "Rainbows! #@use worker_connections=#@worker_connections"
   end
 
+  # TODO: move write_body_* stuff out of Base
+  def write_body_each(client, body)
+    body.each { |chunk| client.write(chunk) }
+    ensure
+      body.respond_to?(:close) and body.close
+  end
+
+  # The sendfile 1.0.0 RubyGem includes IO#sendfile and
+  # IO#sendfile_nonblock, previous versions didn't have
+  # IO#sendfile_nonblock, and IO#sendfile in previous versions
+  # could other threads under 1.8 with large files
+  #
+  # IO#sendfile currently (June 2010) beats 1.9 IO.copy_stream with
+  # non-Linux support and large files on 32-bit.  We still fall back to
+  # IO.copy_stream (if available) if we're dealing with DevFdResponse
+  # objects, though.
+  if IO.method_defined?(:sendfile_nonblock)
+    def write_body_path(client, body)
+      file = Rainbows.body_to_io(body)
+      file.stat.file? ? client.sendfile(file, 0) :
+                        write_body_stream(client, file)
+    end
+  end
+
   if IO.respond_to?(:copy_stream)
-    def write_body(client, body)
-      if body.respond_to?(:to_path)
+    unless method_defined?(:write_body_path)
+      def write_body_path(client, body)
         IO.copy_stream(Rainbows.body_to_io(body), client)
-      else
-        body.each { |chunk| client.write(chunk) }
       end
-      ensure
-        body.respond_to?(:close) and body.close
+    end
+
+    def write_body_stream(client, body)
+      IO.copy_stream(body, client)
     end
   else
+    alias write_body_stream write_body_each
+  end
+
+  if method_defined?(:write_body_path)
     def write_body(client, body)
-      body.each { |chunk| client.write(chunk) }
-      ensure
-        body.respond_to?(:close) and body.close
+      body.respond_to?(:to_path) ?
+        write_body_path(client, body) :
+        write_body_each(client, body)
     end
+  else
+    alias write_body write_body_each
   end
 
-  module_function :write_body
+  module_function :write_body, :write_body_each, :write_body_stream
+  method_defined?(:write_body_path) and module_function(:write_body_path)
 
   def wait_headers_readable(client)
     IO.select([client], nil, nil, G.kato)
diff --git a/lib/rainbows/fiber/base.rb b/lib/rainbows/fiber/base.rb
index 0298948..7e39441 100644
--- a/lib/rainbows/fiber/base.rb
+++ b/lib/rainbows/fiber/base.rb
@@ -72,10 +72,31 @@ module Rainbows
         max.nil? || max > (now + 1) ? 1 : max - now
       end
 
-      def write_body(client, body)
-        body.each { |chunk| client.write(chunk) }
-        ensure
-          body.respond_to?(:close) and body.close
+      # TODO: IO.splice under Linux
+      alias write_body_stream write_body_each
+
+      # the sendfile 1.0.0+ gem includes IO#sendfile_nonblock
+      if ::IO.method_defined?(:sendfile_nonblock)
+        def write_body_path(client, body)
+          file = Rainbows.body_to_io(body)
+          if file.stat.file?
+            sock, off = client.to_io, 0
+            begin
+              off += sock.sendfile_nonblock(file, off, 0x10000)
+            rescue Errno::EAGAIN
+              client.wait_writable
+            rescue EOFError
+              break
+            rescue => e
+              Rainbows::Error.app(e)
+              break
+            end while true
+          else
+            write_body_stream(client, body)
+          end
+        end
+      else
+        alias write_body write_body_each
       end
 
       def wait_headers_readable(client)