rails icon indicating copy to clipboard operation
rails copied to clipboard

Extra stuff after charset in Content-Type disappears

Open r7kamura opened this issue 2 years ago • 2 comments

In my Rails 6 app, I tried to return CSV data with header as a response like this:

class MyController < ApplicationController
  def show
    send_data(
      csv_with_header,
      type: "text/csv; charset=UTF-8; header=present"
    )
  end
end

but header=present has disappeared from its Content-Type.

This is because #content_type= seems to remove the extra stuff after charset=....

e.g.:

response = ActionDispatch::Response.new
response.content_type = "text/csv; charset=utf-16; header=present"
response.headers["Content-Type"] #=> "text/csv; charset=utf-16"

As a workaround, I need to change the above code like this:

@@ -2,7 +2,7 @@
   def show
     send_data(
       csv_with_header,
-      type: "text/csv; charset=utf-16; header=present"
+      type: "text/csv; header=present; charset=utf-16"
     )
   end
 end

At least in Rails (6.0?), we decided to treat extra stuff as part of mime_type at https://github.com/rails/rails/pull/37017. Isn't it strange that what comes before charset is preseved, but what comes after the charset is removed?

response = ActionDispatch::Response.new
response.content_type = "text/csv; a; b; charset=utf-16; c; d"
response.headers["Content-Type"] #=> "text/csv; a; b; charset=utf-16"

Other information about text/csv:

Steps to reproduce

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  # Activate the gem you are reporting the issue against.
  gem "rails", "~> 6.1.0"
end

require "rack/test"
require "action_controller/railtie"

class TestApp < Rails::Application
  config.root = __dir__
  config.hosts << "example.org"
  config.session_store :cookie_store, key: "cookie_store_key"
  secrets.secret_key_base = "secret_key_base"

  config.logger = Logger.new($stdout)
  Rails.logger  = config.logger

  routes.draw do
    get "/" => "test#index"
  end
end

class TestController < ActionController::Base
  include Rails.application.routes.url_helpers

  def index
    send_data(
      "dummy",
      type: "text/csv; charset=utf-16; header=present"
    )
  end
end

require "minitest/autorun"

class BugTest < Minitest::Test
  include Rack::Test::Methods

  def test_content_type_includes_extra_stuff
    get "/"
    assert_includes(last_response.headers["Content-Type"], 'header=present')
  end

  private
    def app
      Rails.application
    end
end

Expected behavior

I expect the above test passes.

Actual behavior

But it fails.

# Running:

I, [2021-11-26T09:22:15.126890 #13131]  INFO -- : Started GET "/" for 127.0.0.1 at 2021-11-26 09:22:15 +0900
F

Failure:
BugTest#test_content_type [test.rb:49]:
Expected "text/csv; charset=utf-16" to include "header=present".


rails test test.rb:47



Finished in 0.069187s, 14.4537 runs/s, 28.9073 assertions/s.
1 runs, 2 assertions, 1 failures, 0 errors, 0 skips

System configuration

Rails version:

6.1.4.1

Ruby version:

$ ruby -v
ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [x86_64-linux]

r7kamura avatar Nov 26 '21 00:11 r7kamura

I submitted https://github.com/rails/rails/pull/43729, but it was automatically closed. If Rails is not going to support this feature, I'm going to close this Issue too. What do you think?

r7kamura avatar Aug 02 '22 00:08 r7kamura

I'll reopen since stale PRs don't get closed now

skipkayhil avatar Aug 02 '22 02:08 skipkayhil