Skip to content

Commit

Permalink
Fix content-length calcuation in Rack:Response#write (#2150)
Browse files Browse the repository at this point in the history
When `Rack::Response` is initialized with an Array, it incorrectly
increments its internal `@length` value and emitted content-length
header on every subsequent write. The more times `write` is called, the
more the error accumulates.

This commit fixes the accumulation bug, and fixes/adds specs to properly
test the scenario where `write` is used multiple times.
  • Loading branch information
mattbrictson committed Jan 31, 2024
1 parent ee27641 commit e50b72a
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ All notable changes to this project will be documented in this file. For info on
- `set_cookie_header` utility now supports the `partitioned` cookie attribute. This is required by Chrome in some embedded contexts. ([#2131](https://github.com/rack/rack/pull/2131), [@flavio-b])
- Remove non-standard status codes 306, 509, & 510 and update descriptions for 413, 422, & 451. ([#2137](https://github.com/rack/rack/pull/2137), [@wtn])
- Add fallback lookup and deprecation warning for obsolete status symbols. ([#2137](https://github.com/rack/rack/pull/2137), [@wtn])
- Fix incorrect content-length header that was emitted when `Rack::Response#write` was used in some situations. ([#2150](https://github.com/rack/rack/pull/2150), [@mattbrictson])

## [3.0.8] - 2023-06-14

Expand Down
2 changes: 2 additions & 0 deletions lib/rack/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ def buffered_body!
@body.each do |part|
@length += part.to_s.bytesize
end

@buffered = true
elsif @body.respond_to?(:each)
# Turn the user supplied body into a buffered array:
body = @body
Expand Down
39 changes: 29 additions & 10 deletions test/spec_response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ def object_with_each.each
status.must_equal 404
end

it "correctly updates content-type when writing when not initialized with body" do
it "correctly updates content-length when writing when initialized without body" do
r = Rack::Response.new
r.write('foo')
r.write('bar')
Expand All @@ -419,20 +419,39 @@ def object_with_each.each
header['content-length'].must_equal '9'
end

it "correctly updates content-type when writing when initialized with body" do
it "correctly updates content-length when writing when initialized with Array body" do
r = Rack::Response.new(["foo"])
r.write('bar')
r.write('baz')
_, header, body = r.finish
str = "".dup; body.each { |part| str << part }
str.must_equal "foobarbaz"
header['content-length'].must_equal '9'
end

it "correctly updates content-length when writing when initialized with String body" do
r = Rack::Response.new("foo")
r.write('bar')
r.write('baz')
_, header, body = r.finish
str = "".dup; body.each { |part| str << part }
str.must_equal "foobarbaz"
header['content-length'].must_equal '9'
end

it "correctly updates content-length when writing when initialized with object body that responds to #each" do
obj = Object.new
def obj.each
yield 'foo'
yield 'bar'
end
["foobar", ["foo", "bar"], obj].each do
r = Rack::Response.new(["foo", "bar"])
r.write('baz')
_, header, body = r.finish
str = "".dup; body.each { |part| str << part }
str.must_equal "foobarbaz"
header['content-length'].must_equal '9'
end
r = Rack::Response.new(obj)
r.write('baz')
r.write('baz')
_, header, body = r.finish
str = "".dup; body.each { |part| str << part }
str.must_equal "foobarbazbaz"
header['content-length'].must_equal '12'
end

it "doesn't return invalid responses" do
Expand Down

0 comments on commit e50b72a

Please sign in to comment.