diff options
author | Eric Wong <normalperson@yhbt.net> | 2009-11-07 12:23:26 -0800 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2009-11-07 12:30:47 -0800 |
commit | 7e35ea595f4742ace9579402323515031d69fc87 (patch) | |
tree | 006c5a6a57c159da466afddc5a7edfb086f6b1cf /lib/rainbows/rev/deferred_response.rb | |
parent | 1266417999aeb939d4e2a7d01aa6730f13cae9fa (diff) | |
download | rainbows-7e35ea595f4742ace9579402323515031d69fc87.tar.gz |
This will make things easier to manage with more Rev-based concurrency models.
Diffstat (limited to 'lib/rainbows/rev/deferred_response.rb')
-rw-r--r-- | lib/rainbows/rev/deferred_response.rb | 70 |
1 files changed, 70 insertions, 0 deletions
diff --git a/lib/rainbows/rev/deferred_response.rb b/lib/rainbows/rev/deferred_response.rb new file mode 100644 index 0000000..d97abbe --- /dev/null +++ b/lib/rainbows/rev/deferred_response.rb @@ -0,0 +1,70 @@ +# -*- encoding: binary -*- +module Rainbows + module Rev + + # this is class is specific to Rev for writing large static files + # or proxying IO-derived objects + class DeferredResponse < ::Rev::IO + include Unicorn + include Rainbows::Const + G = Rainbows::G + HH = Rack::Utils::HeaderHash + + def self.defer!(client, response, out) + body = response.last + headers = HH.new(response[1]) + + # 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 + io = body.to_io if body.respond_to?(:to_io) + io ||= ::IO.new($1.to_i) if body.to_path =~ %r{\A/dev/fd/(\d+)\z} + io ||= File.open(body.to_path, 'rb') + st = io.stat + + if st.socket? || st.pipe? + do_chunk = !!(headers['Transfer-Encoding'] =~ %r{\Achunked\z}i) + do_chunk = false if headers.delete('X-Rainbows-Autochunk') == 'no' + # too tricky to support keepalive/pipelining when a response can + # take an indeterminate amount of time here. + if out.nil? + do_chunk = false + else + out[0] = CONN_CLOSE + end + + io = new(io, client, do_chunk, body).attach(::Rev::Loop.default) + elsif st.file? + headers.delete('Transfer-Encoding') + headers['Content-Length'] ||= st.size.to_s + else # char/block device, directory, whatever... nobody cares + return response + end + client.defer_body(io) + [ 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) + end + + def initialize(io, client, do_chunk, body) + super(io) + @client, @do_chunk, @body = client, do_chunk, body + end + + def on_read(data) + @do_chunk and @client.write(sprintf("%x\r\n", data.size)) + @client.write(data) + @do_chunk and @client.write("\r\n") + end + + def on_close + @do_chunk and @client.write("0\r\n\r\n") + @client.quit + @body.respond_to?(:close) and @body.close + end + end # class DeferredResponse + end # module Rev +end # module Rainbows |