Skip to content

Dont call each when calling body on response to fix #23964#24029

Merged
sgrif merged 1 commit into
rails:masterfrom
rthbound:dont-call-each-when-calling-body-on-response
May 6, 2016
Merged

Dont call each when calling body on response to fix #23964#24029
sgrif merged 1 commit into
rails:masterfrom
rthbound:dont-call-each-when-calling-body-on-response

Conversation

@rthbound

@rthbound rthbound commented Mar 3, 2016

Copy link
Copy Markdown
Contributor
  • 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 Accessing response.body inside any controller action messes up the response #23964.
  • Adds #test_each_isnt_called_if_str_body_is_written to ensure #each 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. This makes tests 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.

@rails-bot

Copy link
Copy Markdown

Thanks for the pull request, and welcome! The Rails team is excited to review your changes, and you should hear from @rafaelfranca (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

@prathamesh-sonpatki prathamesh-sonpatki added this to the 5.0.0 milestone Mar 3, 2016
@rthbound rthbound force-pushed the dont-call-each-when-calling-body-on-response branch 9 times, most recently from bb3ec11 to cf5c01b Compare March 4, 2016 16:49
@rthbound rthbound changed the title Dont call each when calling body on response to fix 23964 Dont call each when calling body on response to fix #23964 Mar 4, 2016
@rthbound rthbound force-pushed the dont-call-each-when-calling-body-on-response branch from cf5c01b to 3ba4f6e Compare March 4, 2016 17:07
@sikachu sikachu assigned sikachu and matthewd and unassigned rafaelfranca and sikachu Mar 11, 2016

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't remove the memoization here, this makes the body method potentially very expensive, and would break streaming responses.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sgrif tests passed locally with the memoization added back. Travis should pick that change up momentarily.

@sgrif

sgrif commented Mar 11, 2016

Copy link
Copy Markdown
Contributor

After looking at this a bit more, I'm not entirely comfortable with this approach. The each method is defined that way for a reason, and I suspect this could interact poorly with streaming responses. I'd prefer that we work to separate out code required for streaming from the buffer.

@rthbound

Copy link
Copy Markdown
Contributor Author

@sgrif are you sure? The only change here is that calling body won't call each. each should be unchanged, and I don't see where response.body is used internally (except in tests).

@sgrif

sgrif commented Mar 11, 2016

Copy link
Copy Markdown
Contributor

The issue is that you can now leave the response in an invalid state simply by calling body on it.

@rthbound

Copy link
Copy Markdown
Contributor Author

@sgrif I beleive that's why we got rid of the memoization that was used when calling #body (which rails is not calling internally, except in tests).

@sgrif

sgrif commented Mar 11, 2016

Copy link
Copy Markdown
Contributor

You're misunderstanding me. You cannot call #each more than once if the response is streaming. The implementation of #each is written to ensure that right now. With this change, a user or gem calling response.body ever with a streaming response will leave things in an invalid state.

@rthbound

Copy link
Copy Markdown
Contributor Author

@sgrif okay, thanks for that. I do understand you now. I checked with @matthewd. He might soon have some suggestions for something "similar-but-still-different" to resolve the issue.

@rthbound

Copy link
Copy Markdown
Contributor Author

@sgrif can you think of a way to show with a test that a user calling response.body for a streaming response in an application or a gem would result in some breakage with these changes? i.e. the test this PR should have broken.

@rthbound rthbound force-pushed the dont-call-each-when-calling-body-on-response branch from 7ec481f to bc2c72f Compare March 12, 2016 04:32
  - 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 rails#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.
@rthbound rthbound force-pushed the dont-call-each-when-calling-body-on-response branch from 26b191e to b43158a Compare March 14, 2016 00:53
@jeremy

jeremy commented Apr 24, 2016

Copy link
Copy Markdown
Member

Is this blocking Rails 5.0.0 release candidate? Is it blocking beta4?

@sgrif

sgrif commented Apr 24, 2016

Copy link
Copy Markdown
Contributor

Blocking RC, yes. Not blocking beta 4

On Sun, Apr 24, 2016, 3:12 PM Jeremy Daer notifications@github.com wrote:

Is this blocking Rails 5.0.0 release candidate? Is it blocking beta4?


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#24029 (comment)

@rthbound

Copy link
Copy Markdown
Contributor Author

@sgrif I'm under the impression that my latest has addressed your concerns.

Also, I did add the tests I mentioned.

Just trying to make I'm on the same page as everyone else.

@sgrif

sgrif commented Apr 25, 2016

Copy link
Copy Markdown
Contributor

I will review later this week, thanks

On Sun, Apr 24, 2016, 8:24 PM Ryan T. Hosford notifications@github.com
wrote:

@sgrif https://github.com/sgrif I'm under the impression that my latest
has addressed your concerns.

Also, I did add the tests I mentioned.

Just trying to make I'm on the same page as everyone else.


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#24029 (comment)

private

def each_chunk(&block)
@buf.each(&block) # extract into own method

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this comment for?

@sgrif

sgrif commented May 6, 2016

Copy link
Copy Markdown
Contributor

OK after a second review I think I have a better understanding. Thanks for working on this

@sgrif sgrif merged commit 21a3b18 into rails:master May 6, 2016
@rthbound

rthbound commented May 6, 2016

Copy link
Copy Markdown
Contributor Author

Thank you @sgrif, and also thanks to @matthewd for working closely with me on this.

@sgrif

sgrif commented May 6, 2016

Copy link
Copy Markdown
Contributor

❤️

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Accessing response.body inside any controller action messes up the response

8 participants