Add support for Rack::ContentLength middleware#16793
Conversation
There was a problem hiding this comment.
Return a copy without the to_ary method so calling flatten on a [response] doesn't recurse infinitely (069bc27).
There was a problem hiding this comment.
I am concerned about the performance impact of this change. Calling undef_method will break method caches, which means people will add the ContentLength middleware and unwittingly ruin performance of their app.
Can we consider an implementation of to_ary that returns nil?
There was a problem hiding this comment.
Pushed 3dbd43300f16f8339fcbbcc77eb8c44cccf92ac7
Feels smelly to satisfy the respond_to? check with no implementation, but Rack::ContentLength is guilty of the opposite and never actually calls to_ary.
There was a problem hiding this comment.
We could return response.body_parts as the implicit conversion. It's always an array, and it seems natural since RackBody serves as a lazy proxy for that array.
There was a problem hiding this comment.
That'll break in the [response].flatten scenario (which may be pathological, but there are tests for) because the response.body_parts array will get flattened down to a string (or nil if empty). This is what lead to my original implementation of returning a new RackBody with to_ary undefined.
Here are those various scenarios played out: https://gist.github.com/javan/934aa107fcc08f72fce8
Having to_ary return nil seems to be the sanest option.
|
cc @jeremy |
|
Another option is to remove the |
There was a problem hiding this comment.
This technically works (it's even covered by Ruby's own tests, iirc), but it's an awkward concession to get some picky-eater middleware to finish its vegetables.
Rack::ContentLength could employ an implicit conversion in the same way, though. Rather than piggybacking on Array's #to_ary, introduce a #to_rack_response_array and use the same #try_convert technique to implicitly convert the response if possible.
Or, it could accept a hint in env or the response headers indicating that the app explicitly wants a Content-Length and knows it isn't providing one.
There was a problem hiding this comment.
Rack::ContentLength isn't actually doing a conversion. It's using body.respond_to?(:to_ary) to implicitly decide if the body is a fixed length or not, but never calling to_ary. Here's the full condition:
!STATUS_WITH_NO_ENTITY_BODY.include?(status.to_i) &&
!headers['Content-Length'] &&
!headers['Transfer-Encoding'] &&
body.respond_to?(:to_ary)IMO, using the headers and status code is sufficient. Streaming responses should take care to set the appropriate headers, like Rails does.
There was a problem hiding this comment.
Agree that status + headers are enough. I'd guess that Rack is trying to accommodate other cases where streaming without a Content-Length or Transfer-Encoding is legit, like HTTP 1.0 responses that are finished when the server closes the connection or HTTP 1.1 responses that use Connection: close. Maybe useful for long polling or SSE.
There was a problem hiding this comment.
I'd guess the same. Hence this PR. 😁
3dbd433 to
66f8997
Compare
|
I'm not sure. We've had issues with its behavior changes before, so I default to 😒 on it. I'd be positive on introducing a |
…middleware Add support for Rack::ContentLength middelware
Currently, Rails is not compatible with
Rack::ContentLengthbecauseActionDispatch::Response::RackBodyinstances don't respond toto_aryand, even though it's not part of the Rack spec, it's how Rack decides if a body has a knowable length. There's some discussion in rack/rack@2b3abc7, which was later reverted leading to #1160. And a little more in rack/rack@9495207.IMO, Rack should remove the
to_arycheck. This change provides immediate support and should continue to work if / when Rack does make the change.