summary refs log tree commit
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2011-01-17 18:35:33 -0800
committerEric Wong <normalperson@yhbt.net>2011-01-17 18:35:33 -0800
commit2dd850e1e5fff8f399f040a0c5ca1ff93179f788 (patch)
tree9a39064a9f833b802a9380bc285b6816e52d69e8
parentba059c17a6a38ac84893487219bf7bca2bed2b8f (diff)
downloadrack-deflate-flush.tar.gz
deflater flushes each chunk of the response deflate-flush
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.
-rw-r--r--lib/rack/deflater.rb7
-rw-r--r--test/spec_deflater.rb38
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")