diff options
author | Eric Wong <e@80x24.org> | 2015-09-21 20:36:16 +0000 |
---|---|---|
committer | Eric Wong <e@80x24.org> | 2015-09-21 21:11:50 +0000 |
commit | f769efe714f5b8b417e0440ce05f8b4e4b504c57 (patch) | |
tree | 3a65d23f8e3eb6fc0b3261c0c380f650f5ebaa17 | |
parent | c28f271d0c91f45e13bfa8f07bed445ef91f41de (diff) | |
download | rack-zlib-disco.tar.gz |
deflater: always finish zlib stream before closing zlib-disco
This helps avoid Zlib::DataError when a client disconnects on the server while the server is writing the response. This fixes the following backtraces on my server: data error (Zlib::DataError) rack/deflater.rb:124:in `close' rack/deflater.rb:124:in `ensure in each' rack/deflater.rb:124:in `each' rack/chunked.rb:23:in `each' ...
-rw-r--r-- | lib/rack/deflater.rb | 3 | ||||
-rw-r--r-- | test/spec_deflater.rb | 25 |
2 files changed, 27 insertions, 1 deletions
diff --git a/lib/rack/deflater.rb b/lib/rack/deflater.rb index 19dc7551..62a11243 100644 --- a/lib/rack/deflater.rb +++ b/lib/rack/deflater.rb @@ -118,8 +118,9 @@ module Rack def each deflator = ::Zlib::Deflate.new(*DEFLATE_ARGS) @body.each { |part| yield deflator.deflate(part, Zlib::SYNC_FLUSH) } - yield deflator.finish + yield fin = deflator.finish ensure + deflator.finish unless fin deflator.close end diff --git a/test/spec_deflater.rb b/test/spec_deflater.rb index 2c7a428c..ba7ec5d3 100644 --- a/test/spec_deflater.rb +++ b/test/spec_deflater.rb @@ -114,6 +114,31 @@ describe Rack::Deflater do end end + it 'does not raise when a client aborts reading' do + app_body = Object.new + class << app_body; def each; yield('foo'); yield('bar'); end; end + opts = { 'skip_body_verify' => true } + verify(200, app_body, 'deflate', opts) do |status, headers, body| + headers.must_equal({ + 'Content-Encoding' => 'deflate', + 'Vary' => 'Accept-Encoding', + 'Content-Type' => 'text/plain' + }) + + buf = [] + inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS) + FakeDisconnect = Class.new(RuntimeError) + assert_raises(FakeDisconnect, "not Zlib::DataError not raised") do + body.each do |part| + buf << inflater.inflate(part) + raise FakeDisconnect + end + end + assert_raises(Zlib::BufError) { inflater.finish } + buf.must_equal(%w(foo)) + end + end + # TODO: This is really just a special case of the above... it 'be able to deflate String bodies' do verify(200, 'Hello world!', 'deflate') do |status, headers, body| |