In a Rails 7.1 app, empty responses are now missing the content-length header when used with Rack 3.1. On Rack 3.0, the header is present with the expected value of 0.
I'm not positive, but this might be a byproduct of #2149 since that deferred setting content-length. If there's no body to iterate, perhaps content-length gets missed?
I don't have a thorough grasp on how Rails and Rack interact in the case of an empty response, so it's possible this issue belongs to Rails instead of Rack. However, I'm starting here since changing the Rack version alone is enough to trigger the behavior difference.
Why this matters
Some HTTP servers revert to chunked encoding when the payload size is unknown. This is inefficient.
Additionally, some HTTP caches use content-length to decide what is and isn't cacheable and its absence can cause the entire request to become uncacheable, causing excess load on app servers.
Reproduction example
# frozen_string_literal: true
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
gem "rails"
# gem "rack", "~> 3.0.0"
gem "rack", "~> 3.1.0"
end
require "action_controller/railtie"
class TestApp < Rails::Application
config.root = __dir__
config.hosts << "www.example.com"
config.secret_key_base = "secret_key_base"
config.action_dispatch.show_exceptions = :rescuable
config.logger = Logger.new($stdout)
Rails.logger = config.logger
routes.draw do
get "/head" => "test#one"
get "/empty" => "test#two"
get "/body" => "test#three"
end
end
class TestController < ActionController::Base
def one
head 200
end
def two
render plain: ''
end
def three
render plain: 'a', layout: false
end
end
require "minitest/autorun"
class TestControllerTest < ActionDispatch::IntegrationTest
# works on rack 3.0
# content-length key is missing on rack 3.1
def test_head
get "/head"
assert_response 200
assert_equal '0', headers['content-length']
# assert headers.key?('content-length')
end
# works on rack 3.0
# content-length key is missing on rack 3.1
def test_empty_body
get "/empty"
assert_response 200
assert_equal '0', headers['content-length']
# assert headers.key?('content-length')
end
# works on rack 3.0 and 3.1
def test_with_body
get "/body"
assert_response 200
assert_equal '1', headers['content-length']
end
private
def app
Rails.application
end
end
In a Rails 7.1 app, empty responses are now missing the
content-lengthheader when used with Rack 3.1. On Rack 3.0, the header is present with the expected value of0.I'm not positive, but this might be a byproduct of #2149 since that deferred setting
content-length. If there's no body to iterate, perhapscontent-lengthgets missed?I don't have a thorough grasp on how Rails and Rack interact in the case of an empty response, so it's possible this issue belongs to Rails instead of Rack. However, I'm starting here since changing the Rack version alone is enough to trigger the behavior difference.
Why this matters
Some HTTP servers revert to chunked encoding when the payload size is unknown. This is inefficient.
Additionally, some HTTP caches use
content-lengthto decide what is and isn't cacheable and its absence can cause the entire request to become uncacheable, causing excess load on app servers.Reproduction example