Skip to content

Multiple writes to Rack::Response result in incorrect content-length header in Rack 3 #2148

@mattbrictson

Description

@mattbrictson

Problem

After upgrading to Rack 3, I found that Rack::Response generates an incorrect content-length header when:

  • it is constructed with an empty array for the body, and
  • write is called multiple times

Here is a simple reproduction script:

require "rack"

status = 200
headers = {}
response = Rack::Response.new([], status, headers)

# Write 11 total bytes
response.write "hello"
response.write "world"
response.write "!"

# Resulting content-length should be 11, but is 26
response.headers
# => {"content-length"=>"26"}

Underlying cause

When Rack::Response is constructed with an empty array, it sets this internal state:

@buffered = nil # undetermined as of yet.

This causes the value of @length to accumulate on every call to Rack::Response#write:

rack/lib/rack/response.rb

Lines 317 to 323 in 0cd4d40

if @buffered.nil?
if @body.is_a?(Array)
# The user supplied body was an array:
@body = @body.compact
@body.each do |part|
@length += part.to_s.bytesize
end

When chunked? is false, @length is incremented yet again, and then emitted as a content-length header:

rack/lib/rack/response.rb

Lines 348 to 350 in 0cd4d40

unless chunked?
@length += chunk.bytesize
set_header(CONTENT_LENGTH, @length.to_s)

The result is that the content-length value is too large, and the error gets larger on every call to write.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions