From 2dd850e1e5fff8f399f040a0c5ca1ff93179f788 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 17 Jan 2011 18:35:33 -0800 Subject: deflater flushes each chunk of the response This allows clients to receive streaming response bodies as they're generated by the application, not only when it's ideal for zlib. Space-efficiency is hurt somewhat, but there's no other way to allow this middleware to work without completely breaking otherwise valid applications. --- lib/rack/deflater.rb | 7 +++++-- test/spec_deflater.rb | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/lib/rack/deflater.rb b/lib/rack/deflater.rb index ad0f5316..e7da3a3a 100644 --- a/lib/rack/deflater.rb +++ b/lib/rack/deflater.rb @@ -60,7 +60,10 @@ module Rack @writer = block gzip =::Zlib::GzipWriter.new(self) gzip.mtime = @mtime - @body.each { |part| gzip.write(part) } + @body.each { |part| + gzip.write(part) + gzip.flush + } @body.close if @body.respond_to?(:close) gzip.close @writer = nil @@ -86,7 +89,7 @@ module Rack def each deflater = ::Zlib::Deflate.new(*DEFLATE_ARGS) - @body.each { |part| yield deflater.deflate(part) } + @body.each { |part| yield deflater.deflate(part, Zlib::SYNC_FLUSH) } @body.close if @body.respond_to?(:close) yield deflater.finish nil diff --git a/test/spec_deflater.rb b/test/spec_deflater.rb index 43c8c95f..0c9d060b 100644 --- a/test/spec_deflater.rb +++ b/test/spec_deflater.rb @@ -35,6 +35,25 @@ describe Rack::Deflater do inflate(buf).should.equal("foobar") end + should "flush deflated chunks to the client as they become ready" do + body = Object.new + class << body; def each; yield("foo"); yield("bar"); end; end + + response = build_response(200, body, "deflate") + + response[0].should.equal(200) + response[1].should.equal({ + "Content-Encoding" => "deflate", + "Vary" => "Accept-Encoding" + }) + buf = [] + inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS) + response[2].each { |part| buf << inflater.inflate(part) } + buf << inflater.finish + buf.delete_if { |part| part.empty? } + buf.should.equal(%w(foo bar)) + end + # TODO: This is really just a special case of the above... should "be able to deflate String bodies" do response = build_response(200, "Hello world!", "deflate") @@ -69,6 +88,25 @@ describe Rack::Deflater do gz.close end + should "flush gzipped chunks to the client as they become ready" do + body = Object.new + class << body; def each; yield("foo"); yield("bar"); end; end + + response = build_response(200, body, "gzip") + + response[0].should.equal(200) + response[1].should.equal({ + "Content-Encoding" => "gzip", + "Vary" => "Accept-Encoding" + }) + buf = [] + inflater = Zlib::Inflate.new(Zlib::MAX_WBITS + 32) + response[2].each { |part| buf << inflater.inflate(part) } + buf << inflater.finish + buf.delete_if { |part| part.empty? } + buf.should.equal(%w(foo bar)) + end + should "be able to fallback to no deflation" do response = build_response(200, "Hello world!", "superzip") -- cgit v1.2.3-24-ge0c7