1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
- Adds #each_chunk to ActionDispatch::Response. it's a method which
    will be called by ActionDispatch::Response#each.
  - Make Response#each a proper method instead of delegating to @stream
  - In Live, instead of overriding #each, override #each_chunk.
  - `#each` should just spit out @str_body if it's already set
  - Adds #test_set_header_after_read_body_during_action
    to prove this fixes #23964
  - Adds #test_each_isnt_called_if_str_body_is_written to
    ensure #each_chunk is not called when @str_body is available
  - Call `@response.sent!` in AC::TestCase's #perform so a test response
    acts a bit more like a real response. Makes test that call  `#assert_stream_closed`
    pass again.
  - Additionally assert `#committed?` in `#assert_stream_closed`
  - Make test that was calling @response.stream.each pass again by
    calling @response.each instead.
This commit is contained in:
Ryan T. Hosford 2016-03-03 00:43:18 -06:00
parent 7b96d860fd
commit b43158afba
6 changed files with 70 additions and 19 deletions

View file

@ -163,14 +163,6 @@ module ActionController
end
end
def each
@response.sending!
while str = @buf.pop
yield str
end
@response.sent!
end
# Write a 'close' event to the buffer; the producer/writing thread
# uses this to notify us that it's finished supplying content.
#
@ -210,6 +202,14 @@ module ActionController
def call_on_error
@error_callback.call
end
private
def each_chunk(&block)
while str = @buf.pop
yield str
end
end
end
class Response < ActionDispatch::Response #:nodoc: all

View file

@ -554,6 +554,8 @@ module ActionController
end
@request.query_string = ''
@response.sent!
@response
end

View file

@ -68,7 +68,13 @@ module ActionDispatch # :nodoc:
alias_method :headers, :header
delegate :[], :[]=, :to => :@header
delegate :each, :to => :@stream
def each(&block)
sending!
x = @stream.each(&block)
sent!
x
end
CONTENT_TYPE = "Content-Type".freeze
SET_COOKIE = "Set-Cookie".freeze
@ -97,10 +103,10 @@ module ActionDispatch # :nodoc:
def body
@str_body ||= begin
buf = ''
each { |chunk| buf << chunk }
buf
end
buf = ''
each { |chunk| buf << chunk }
buf
end
end
def write(string)
@ -112,10 +118,13 @@ module ActionDispatch # :nodoc:
end
def each(&block)
@response.sending!
x = @buf.each(&block)
@response.sent!
x
if @str_body
return enum_for(:each) unless block_given?
yield @str_body
else
each_chunk(&block)
end
end
def abort
@ -129,6 +138,12 @@ module ActionDispatch # :nodoc:
def closed?
@closed
end
private
def each_chunk(&block)
@buf.each(&block) # extract into own method
end
end
def self.create(status = 200, header = {}, body = [], default_headers: self.default_headers)

View file

@ -246,7 +246,8 @@ module ActionController
def assert_stream_closed
assert response.stream.closed?, 'stream should be closed'
assert response.sent?, 'stream should be sent'
assert response.committed?, 'response should be committed'
assert response.sent?, 'response should be sent'
end
def capture_log_output

View file

@ -65,7 +65,7 @@ module ActionController
latch = Concurrent::CountDownLatch.new
t = Thread.new {
@response.stream.each do
@response.each do
latch.count_down
end
}

View file

@ -37,6 +37,39 @@ class ResponseTest < ActiveSupport::TestCase
assert_equal "closed stream", e.message
end
def test_each_isnt_called_if_str_body_is_written
# Controller writes and reads response body
each_counter = 0
@response.body = Object.new.tap {|o| o.singleton_class.send(:define_method, :each) { |&block| each_counter += 1; block.call 'foo' } }
@response['X-Foo'] = @response.body
assert_equal 1, each_counter, "#each was not called once"
# Build response
status, headers, body = @response.to_a
assert_equal 200, status
assert_equal "foo", headers['X-Foo']
assert_equal "foo", body.each.to_a.join
# Show that #each was not called twice
assert_equal 1, each_counter, "#each was not called once"
end
def test_set_header_after_read_body_during_action
@response.body
# set header after the action reads back @response.body
@response['x-header'] = "Best of all possible worlds."
# the response can be built.
status, headers, body = @response.to_a
assert_equal 200, status
assert_equal "", body.body
assert_equal "Best of all possible worlds.", headers['x-header']
end
def test_read_body_during_action
@response.body = "Hello, World!"