1010module JRuby
1111 module Rack
1212 # Takes a Rack response to map it into the Servlet world.
13- #
13+ #
1414 # Assumes servlet containers auto-handle chunking when the output stream
1515 # gets flushed. Thus de-chunks data if Rack chunked them, to disable this
1616 # behavior execute the following before delivering responses :
@@ -22,7 +22,12 @@ class Response
2222 include org . jruby . rack . RackResponse
2323 java_import 'java.nio.ByteBuffer'
2424 java_import 'java.nio.channels.Channels'
25-
25+
26+ @@swallow_client_abort = true
27+ # Whether we swallow client abort exceptions (EOF received on the socket).
28+ def self . swallow_client_abort? ; @@swallow_client_abort ; end
29+ def self . swallow_client_abort = ( flag ) ; @@swallow_client_abort = !! flag ; end
30+
2631 @@dechunk = nil
2732 # Whether responses should de-chunk data (when chunked response detected).
2833 def self . dechunk? ; @@dechunk ; end
@@ -42,7 +47,7 @@ def self.dechunk=(flag); @@dechunk = !! flag; end
4247 def self . channel_chunk_size ; @@channel_chunk_size ; end
4348 def self . channel_chunk_size = ( size ) ; @@channel_chunk_size = size ; end
4449 def channel_chunk_size ; self . class . channel_chunk_size ; end
45-
50+
4651 @@channel_buffer_size = 16 * 1024 # 16 kB
4752 # Returns a byte buffer size that will be allocated when copying between
4853 # channels. This usually won't happen at all (unless you return an exotic
@@ -52,7 +57,7 @@ def channel_chunk_size; self.class.channel_chunk_size; end
5257 def self . channel_buffer_size ; @@channel_buffer_size ; end
5358 def self . channel_buffer_size = ( size ) ; @@channel_buffer_size = size ; end
5459 def channel_buffer_size ; self . class . channel_buffer_size ; end
55-
60+
5661 # Expects a Rack response: [status, headers, body].
5762 def initialize ( array )
5863 @status , @headers , @body = *array
@@ -93,7 +98,7 @@ def respond(response)
9398 write_body ( response )
9499 end
95100 end
96-
101+
97102 # Writes the response status.
98103 # @see #respond
99104 def write_status ( response )
@@ -114,7 +119,7 @@ def write_headers(response)
114119 # setContentLength(int) ... addHeader must be used for large files (>2GB)
115120 response . setContentLength ( length ) if ! chunked? && length < 2_147_483_648
116121 else
117- # servlet container auto handle chunking when response is flushed
122+ # servlet container auto handle chunking when response is flushed
118123 # (and Content-Length headers has not been set) :
119124 next if key == TRANSFER_ENCODING && skip_encoding_header? ( value )
120125 # NOTE: effectively the same as `v.split("\n").each` which is what
@@ -147,16 +152,16 @@ def write_body(response)
147152 @body . call response . getOutputStream
148153 elsif @body . respond_to? ( :to_path ) # send_file
149154 send_file @body . to_path , response
150- elsif @body . respond_to? ( :to_channel ) &&
155+ elsif @body . respond_to? ( :to_channel ) &&
151156 ! object_polluted_with_anyio? ( @body , :to_channel )
152157 body = @body . to_channel # so that we close the channel
153158 transfer_channel body , response . getOutputStream
154- elsif @body . respond_to? ( :to_inputstream ) &&
159+ elsif @body . respond_to? ( :to_inputstream ) &&
155160 ! object_polluted_with_anyio? ( @body , :to_inputstream )
156161 body = @body . to_inputstream # so that we close the stream
157162 body = Channels . newChannel ( body ) # closing the channel closes the stream
158163 transfer_channel body , response . getOutputStream
159- elsif @body . respond_to? ( :body_parts ) && @body . body_parts . respond_to? ( :to_channel ) &&
164+ elsif @body . respond_to? ( :body_parts ) && @body . body_parts . respond_to? ( :to_channel ) &&
160165 ! object_polluted_with_anyio? ( @body . body_parts , :to_channel )
161166 # ActionDispatch::Response "raw" body access in case it's a File
162167 body = @body . body_parts . to_channel # so that we close the channel
@@ -178,44 +183,43 @@ def write_body(response)
178183 # HACK: deal with objects that don't comply with Rack specification
179184 @body = [ @body . to_s ]
180185 retry
181- rescue NativeException => e
182- # Don't needlessly raise errors because of client abort exceptions
183- raise unless e . cause . toString =~ /(clientabortexception|broken pipe)/i
186+ rescue java . io . IOException => e
187+ raise e if ! client_abort_exception? ( e ) || ! self . class . swallow_client_abort?
184188 ensure
185189 @body . close if @body . respond_to? ( :close )
186190 body && body . close rescue nil
187191 end
188192 end
189193
190194 protected
191-
195+
192196 # @return [true, false] whether a chunked encoding is detected
193197 def chunked?
194198 return @chunked unless @chunked . nil?
195199 @chunked = !! ( @headers && @headers [ TRANSFER_ENCODING ] == 'chunked' )
196200 end
197-
201+
198202 # @return [true, false] whether output (body) should be flushed after each
199203 # written (yielded) line
200204 # @see #chunked?
201205 def flush?
202206 chunked? || ! ( @headers && @headers [ 'Content-Length' ] )
203207 end
204-
208+
205209 # Whether de-chunking (a chunked Rack response) should be performed.
206210 # @see JRuby::Rack::Response#dechunk?
207211 # @see #chunked?
208212 def dechunk?
209213 self . class . dechunk? && chunked?
210214 end
211-
215+
212216 # Sends a file when a Rails/Rack file response (`body.to_path`) is detected.
213217 # This allows for potential application server overrides when file streaming.
214218 # By default JRuby-Rack will stream the file using a (native) file channel.
215- #
219+ #
216220 # @param path the file path
217221 # @param response the response environment
218- #
222+ #
219223 # @note That this is not related to `Rack::Sendfile` support, since if you
220224 # have configured *sendfile.type* (e.g. to Apache's "X-Sendfile") this part
221225 # would not have been executing at all.
@@ -229,18 +233,22 @@ def send_file(path, response)
229233 input . close rescue nil
230234 end
231235 end
232-
236+
233237 private
234-
238+
239+ def client_abort_exception? ( ioe )
240+ ioe . inspect =~ /(ClientAbortException|EofException|broken pipe)/i
241+ end
242+
235243 def skip_encoding_header? ( value )
236244 value == 'chunked' && @@dechunk != false
237245 end
238-
246+
239247 def write_body_dechunked ( output_stream )
240248 # NOTE: due Rails 3.2 stream-ed rendering http://git.io/ooCOtA#L223
241249 # Only required if the patch at jruby/rack/chunked.rb is not applied ...
242250 term = "\r \n " ; tail = "0#{ term } #{ term } " . freeze
243- # we assume no support here for chunk-extensions e.g.
251+ # we assume no support here for chunk-extensions e.g.
244252 # chunk = chunk-size [ chunk-extension ] CRLF chunk-data CRLF
245253 # no need to be handled - we simply unwrap what Rails chunked :
246254 chunk = /^([0-9a-fA-F]+)#{ Regexp . escape ( term ) } (.+)#{ Regexp . escape ( term ) } /mo
@@ -259,7 +267,7 @@ def write_body_dechunked(output_stream)
259267 output_stream . flush
260268 end
261269 end
262-
270+
263271 def transfer_channel ( channel , output_stream )
264272 output_channel = Channels . newChannel output_stream
265273 if channel . respond_to? ( :transfer_to ) && channel_chunk_size # FileChannel
@@ -280,11 +288,11 @@ def transfer_channel(channel, output_stream)
280288 end
281289 end
282290 end
283-
291+
284292 # Fixnum should not have this method, and it shouldn't be on Object
285293 @@object_polluted = ( Fixnum . method ( :to_channel ) . owner == Object ) rescue nil # :nodoc
286-
287- # See http://bugs.jruby.org/5444 - we need to account for pre-1.6 JRuby
294+
295+ # See http://bugs.jruby.org/5444 - we need to account for pre-1.6 JRuby
288296 # where Object was polluted with #to_channel ( by IOJavaAddions.AnyIO )
289297 def object_polluted_with_anyio? ( obj , meth ) # :nodoc
290298 @@object_polluted && begin
@@ -295,7 +303,7 @@ def object_polluted_with_anyio?(obj, meth) # :nodoc
295303 false
296304 end
297305 end
298-
306+
299307 end
300308 end
301309end
0 commit comments