Skip to content

Commit

Permalink
Fix content-length calcuation in Rack:Response#write
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 30, 2024
1 parent ee27641 commit bc337da
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 15 deletions.
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
34 changes: 19 additions & 15 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,24 @@ def object_with_each.each
header['content-length'].must_equal '9'
end

it "correctly updates content-type when writing when initialized with body" 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
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 "doesn't return invalid responses" do
Expand Down

0 comments on commit bc337da

Please sign in to comment.