Permalink
Browse files

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.
  • Loading branch information...
1 parent c6c5060 commit 37d2b2fe40667eb0a7ff89111713832be934aa9f Eric Wong committed with tenderlove Jan 18, 2011
Showing with 43 additions and 2 deletions.
  1. +5 −2 lib/rack/deflater.rb
  2. +38 −0 test/spec_deflater.rb
View
@@ -60,7 +60,10 @@ def each(&block)
@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 @@ def initialize(body)
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
View
@@ -35,6 +35,25 @@ class << body; def each; yield("foo"); yield("bar"); end; end
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 @@ class << body; def each; yield("foo"); yield("bar"); end; end
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")

0 comments on commit 37d2b2f

Please sign in to comment.