Search code examples
httprequestresponseelixirphoenix-framework

Phoenix tests don't accept capitalized response Headers


I've been playing around with Phoenix and I want to provide a download link to a PDF file:

The following code works fine in my dev environment. When I click on the link a PDF file is downloaded.

case  File.read(localpath) do
  {:ok, pdf_content} ->
    conn
    |> put_resp_header("Content-Type", "application/pdf")
    |> put_resp_header("Content-Disposition", ~s[attachment; filename="#{file}"])
    |>200, pdf_content)
  {:error, _} ->
    conn
    |>:not_found, "Not Found")
end

However when I run tests to verify the behaviour I'll get an error:

 ** (Plug.Conn.InvalidHeaderError) header key is not lowercase: "Content-Type"
 stacktrace:
   (plug) lib/plug/conn.ex:957: Plug.Conn.validate_header_key!/2
   (plug) lib/plug/conn.ex:556: Plug.Conn.put_resp_header/3

This seems weird to me for 2 reasons:

  1. Why has the Header have to be in lower characters?
  2. Why is the behaviour different in dev and in test environment?

Solution

  • The reason this error is only raised in the test environment can be seen in the source code for Plug.Conn, specifically these lines:

    defp validate_header_key!({Plug.Adapters.Test.Conn, _}, key) do
      unless valid_header_key?(key) do
        raise InvalidHeaderError, message: "header key is not lowercase: " <> inspect(key)
      end
    end
    
    defp validate_header_key!(_adapter, _key) do
      :ok
    end
    

    As this code shows, the header key is actually validated only if the adapter is Plug's test adapter. The reason this only happens in the test environment is because it can be expensive to perform these validations on the headers and so they are skipped in non-test environments. This commit is the commit where the restriction on validating only in the test environment was introduced.

    By the way, the header doesn't have to be lowercase (as you can tell by the fact that it works in non-test environments), but I think that by Plug's convention it should be.