From f7c6e2c8aa8f991252224a8464cf3196263849e6 Mon Sep 17 00:00:00 2001 From: Hartley McGuire Date: Fri, 9 Jun 2023 18:43:00 -0400 Subject: [PATCH] Fix duplicate Content-Type header with Rack 2 Previously, `ActionDispatch::Static` would always merge a "content-type" header into the headers returned from `Rack::Files`. However, this would potentially lead to both a "Content-Type" header and a "content-type" header when using Rack 2. This commit fixes the issue by using `Rack::CONTENT_TYPE` to determine which version of the header to set in `ActionDispatch::Static`. In both versions of Rack it will use the same version of the header as `Rack::Files`. The tests added have to use `@app.call` instead of `get()`/`Rack::MockRequest` because `Rack::Response` actually does the correct thing already by using `Rack::Util::HeaderHash` so it covers up the issue in tests. --- actionpack/lib/action_dispatch/middleware/static.rb | 2 +- actionpack/test/dispatch/static_test.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb index 0103ddd965d77..81a00e3b6e94f 100644 --- a/actionpack/lib/action_dispatch/middleware/static.rb +++ b/actionpack/lib/action_dispatch/middleware/static.rb @@ -108,7 +108,7 @@ def find_file(path_info, accept_encoding:) end def try_files(filepath, content_type, accept_encoding:) - headers = { "content-type" => content_type } + headers = { Rack::CONTENT_TYPE => content_type } if compressible? content_type try_precompressed_files filepath, headers, accept_encoding: accept_encoding diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb index cc81b4f497584..892a8735f118d 100644 --- a/actionpack/test/dispatch/static_test.rb +++ b/actionpack/test/dispatch/static_test.rb @@ -199,6 +199,18 @@ def test_does_not_modify_path_info assert_equal file_name, env["PATH_INFO"] end + def test_only_set_one_content_type + file_name = "/gzip/foo.zoo" + gzip_env = { "PATH_INFO" => file_name, "HTTP_ACCEPT_ENCODING" => "gzip", "REQUEST_METHOD" => "GET" } + response = @app.call(gzip_env) + + env = { "PATH_INFO" => file_name, "REQUEST_METHOD" => "GET" } + default_response = @app.call(env) + + assert_equal 1, response[1].slice("Content-Type", "content-type").size + assert_equal 1, default_response[1].slice("Content-Type", "content-type").size + end + def test_serves_gzip_with_proper_content_type_fallback file_name = "/gzip/foo.zoo" response = get(file_name, "HTTP_ACCEPT_ENCODING" => "gzip")