about summary refs log tree commit homepage
path: root/lib/rainbows/rev/client.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rainbows/rev/client.rb')
-rw-r--r--lib/rainbows/rev/client.rb75
1 files changed, 75 insertions, 0 deletions
diff --git a/lib/rainbows/rev/client.rb b/lib/rainbows/rev/client.rb
new file mode 100644
index 0000000..d191787
--- /dev/null
+++ b/lib/rainbows/rev/client.rb
@@ -0,0 +1,75 @@
+# -*- encoding: binary -*-
+require 'rainbows/ev_core'
+module Rainbows
+  module Rev
+
+    include Base
+
+    class Client < ::Rev::IO
+      include Rainbows::EvCore
+      G = Rainbows::G
+
+      def initialize(io)
+        G.cur += 1
+        super(io)
+        post_init
+        @deferred_bodies = [] # for (fast) regular files only
+      end
+
+      # queued, optional response bodies, it should only be unpollable "fast"
+      # devices where read(2) is uninterruptable.  Unfortunately, NFS and ilk
+      # are also part of this.  We'll also stick DeferredResponse bodies in
+      # here to prevent connections from being closed on us.
+      def defer_body(io)
+        @deferred_bodies << io
+        on_write_complete unless @hp.headers? # triggers a write
+      end
+
+      def app_call
+        begin
+          (@env[RACK_INPUT] = @input).rewind
+          @env[REMOTE_ADDR] = @remote_addr
+          response = APP.call(@env.update(RACK_DEFAULTS))
+          alive = @hp.keepalive? && G.alive
+          out = [ alive ? CONN_ALIVE : CONN_CLOSE ] if @hp.headers?
+
+          DeferredResponse.write(self, response, out)
+          if alive
+            @env.clear
+            @hp.reset
+            @state = :headers
+            # keepalive requests are always body-less, so @input is unchanged
+            @hp.headers(@env, @buf) and next
+          else
+            quit
+          end
+          return
+        end while true
+      end
+
+      def on_write_complete
+        if body = @deferred_bodies.first
+          return if DeferredResponse === body
+          begin
+            begin
+              write(body.sysread(CHUNK_SIZE))
+            rescue EOFError # expected at file EOF
+              @deferred_bodies.shift
+              body.close
+              close if :close == @state && @deferred_bodies.empty?
+            end
+          rescue => e
+            handle_error(e)
+          end
+        else
+          close if :close == @state
+        end
+      end
+
+      def on_close
+        G.cur -= 1
+      end
+
+    end # module Client
+  end # module Rev
+end # module Rainbows