Skip to content
This repository

Add #close to GzipStream and DeflateStream classes #531

Closed
wants to merge 1 commit into from

2 participants

Matt Colyer James Tucker
Matt Colyer

On Heroku when using unicorn, the deflater middleware will occasionally fail to properly close the body. This results in deadlock errors.

By adding close methods to these classes, we ensure that unicorn calls close in all cases. I'm guessing that the occasional failures are caused by requests that are terminated prematurely and the ensure block within the each method isn't triggered. This fixes that because unicorn wraps the method which calls each with an ensure clause that calls close.

Matt Colyer Add #close to GzipStream and DeflateStream classes
On Heroku when using unicorn, the deflater middleware will occasionally
fail to properly close the body. This results in deadlock errors.

By adding close methods to these classes, we ensure that unicorn calls
close in all cases. I'm guessing that the occasional failures are caused
by requests that are terminated prematurely and the ensure block within
the each method isn't triggered. This fixes that because unicorn wraps
the method which calls each with an ensure clause that calls close.
1d3a7d4
James Tucker
Owner
raggi commented April 21, 2013

Why did you change the order of closure?

Matt Colyer

I'm not sure that I understand, which lines are you referring to?

James Tucker
Owner
raggi commented April 21, 2013

You reversed the order of body.close and stream.close in each case.

Matt Colyer

My thinking was that I absolutely wanted to be sure that all of the content was flushed before closing the body. As you can see from Line 94, things aren't always happening synchronously and so I figured that it was better to be safe.

Does this have a negative effect somewhere else?

James Tucker
Owner
raggi commented April 22, 2013

Line 94 occurs because GzipWriter's close calls close on the io object (self in this case).

I apologize for doing it to you, but I have superseded this implementation and will be committing it to master.

James Tucker raggi closed this pull request from a commit April 22, 2013
James Tucker delfater: ensure that parent body is always closed
 * Fixes a bug where body is not enumerated (i.e. HEAD), and as such is never
   closed.
 * Users suffering from the above bug are encouraged to investigate their
   middleware order.
 * Supersedes and closes #531.
7bda8d4
James Tucker raggi closed this in 7bda8d4 April 22, 2013
Matt Colyer

I'd rather see it fixed, so no worries about not using the commit. I'll keep an eye out for the next release and ensure that it resolves the issue. Thanks for the help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 1 unique commit by 1 author.

Mar 21, 2013
Matt Colyer Add #close to GzipStream and DeflateStream classes
On Heroku when using unicorn, the deflater middleware will occasionally
fail to properly close the body. This results in deadlock errors.

By adding close methods to these classes, we ensure that unicorn calls
close in all cases. I'm guessing that the occasional failures are caused
by requests that are terminated prematurely and the ensure block within
the each method isn't triggered. This fixes that because unicorn wraps
the method which calls each with an ensure clause that calls close.
1d3a7d4
This page is out of date. Refresh to see the latest.

Showing 1 changed file with 23 additions and 10 deletions. Show diff stats Hide diff stats

  1. 33  lib/rack/deflater.rb
33  lib/rack/deflater.rb
@@ -72,21 +72,30 @@ def initialize(body, mtime)
72 72
 
73 73
       def each(&block)
74 74
         @writer = block
75  
-        gzip  =::Zlib::GzipWriter.new(self)
76  
-        gzip.mtime = @mtime
  75
+        @gzip =::Zlib::GzipWriter.new(self)
  76
+        @gzip.mtime = @mtime
77 77
         @body.each { |part|
78  
-          gzip.write(part)
79  
-          gzip.flush
  78
+          @gzip.write(part)
  79
+          @gzip.flush
80 80
         }
81 81
       ensure
82  
-        @body.close if @body.respond_to?(:close)
83  
-        gzip.close
  82
+        close
84 83
         @writer = nil
85 84
       end
86 85
 
87 86
       def write(data)
88 87
         @writer.call(data)
89 88
       end
  89
+
  90
+      def close
  91
+        begin
  92
+          @gzip.close if @gzip && !@gzip.closed?
  93
+        rescue Zlib::GzipFile::Error
  94
+          # For some reason this error is still thrown despite the .closed?
  95
+          # check on the previous line.
  96
+        end
  97
+        @body.close if @body.respond_to?(:close)
  98
+      end
90 99
     end
91 100
 
92 101
     class DeflateStream
@@ -103,13 +112,17 @@ def initialize(body)
103 112
       end
104 113
 
105 114
       def each
106  
-        deflater = ::Zlib::Deflate.new(*DEFLATE_ARGS)
107  
-        @body.each { |part| yield deflater.deflate(part, Zlib::SYNC_FLUSH) }
108  
-        yield deflater.finish
  115
+        @deflater = ::Zlib::Deflate.new(*DEFLATE_ARGS)
  116
+        @body.each { |part| yield @deflater.deflate(part, Zlib::SYNC_FLUSH) }
  117
+        yield @deflater.finish
109 118
         nil
110 119
       ensure
  120
+        close
  121
+      end
  122
+
  123
+      def close
  124
+        @deflater.close if @deflater && !@deflater.closed?
111 125
         @body.close if @body.respond_to?(:close)
112  
-        deflater.close
113 126
       end
114 127
     end
115 128
   end
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.