Skip to content

Commit

Permalink
Add :downcase_request_headers option to HTTP1.connect (#399)
Browse files Browse the repository at this point in the history
  • Loading branch information
DunyaKokoschka authored Feb 12, 2024
1 parent ce8102b commit 74e0ec6
Show file tree
Hide file tree
Showing 9 changed files with 321 additions and 121 deletions.
136 changes: 136 additions & 0 deletions lib/mint/core/headers.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
defmodule Mint.Core.Headers do
@moduledoc false

@type canonical() ::
{original_name :: String.t(), canonical_name :: String.t(), value :: String.t()}
@type raw() :: {original_name :: String.t(), value :: String.t()}

@unallowed_trailers MapSet.new([
"content-encoding",
"content-length",
"content-range",
"content-type",
"trailer",
"transfer-encoding",

# Control headers (https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7231.html#rfc.section.5.1)
"cache-control",
"expect",
"host",
"max-forwards",
"pragma",
"range",
"te",

# Conditionals (https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7231.html#rfc.section.5.2)
"if-match",
"if-none-match",
"if-modified-since",
"if-unmodified-since",
"if-range",

# Authentication/authorization (https://tools.ietf.org/html/rfc7235#section-5.3)
"authorization",
"proxy-authenticate",
"proxy-authorization",
"www-authenticate",

# Cookie management (https://tools.ietf.org/html/rfc6265)
"cookie",
"set-cookie",

# Control data (https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7231.html#rfc.section.7.1)
"age",
"cache-control",
"expires",
"date",
"location",
"retry-after",
"vary",
"warning"
])

@spec from_raw([raw()]) :: [canonical()]
def from_raw(headers) do
Enum.map(headers, fn {name, value} -> {name, lower_raw(name), value} end)
end

@spec to_raw([canonical()], boolean()) :: [raw()]
def to_raw(headers, _case_sensitive = true) do
Enum.map(headers, fn {name, _canonical_name, value} -> {name, value} end)
end

def to_raw(headers, _case_sensitive = false) do
Enum.map(headers, fn {_name, canonical_name, value} ->
{canonical_name, value}
end)
end

@spec find([canonical()], String.t()) :: {String.t(), String.t()} | nil
def find(headers, name) do
case List.keyfind(headers, name, 1) do
nil -> nil
{name, _canonical_name, value} -> {name, value}
end
end

@spec replace([canonical()], String.t(), String.t(), String.t()) ::
[canonical()]
def replace(headers, new_name, canonical_name, value) do
List.keyreplace(headers, canonical_name, 1, {new_name, canonical_name, value})
end

@spec has?([canonical()], String.t()) :: boolean()
def has?(headers, name) do
List.keymember?(headers, name, 1)
end

@spec put_new([canonical()], String.t(), String.t(), String.t() | nil) ::
[canonical()]
def put_new(headers, _name, _canonical_name, nil) do
headers
end

def put_new(headers, name, canonical_name, value) do
if List.keymember?(headers, canonical_name, 1) do
headers
else
[{name, canonical_name, value} | headers]
end
end

@spec put_new([canonical()], String.t(), String.t(), (-> String.t())) ::
[canonical()]
def put_new_lazy(headers, name, canonical_name, fun) do
if List.keymember?(headers, canonical_name, 1) do
headers
else
[{name, canonical_name, fun.()} | headers]
end
end

@spec find_unallowed_trailer([canonical()]) :: String.t() | nil
def find_unallowed_trailer(headers) do
Enum.find_value(headers, fn
{raw_name, canonical_name, _value} ->
if canonical_name in @unallowed_trailers do
raw_name
end
end)
end

@spec remove_unallowed_trailer([raw()]) :: [raw()]
def remove_unallowed_trailer(headers) do
Enum.reject(headers, fn {name, _value} -> name in @unallowed_trailers end)
end

@spec lower_raw(String.t()) :: String.t()
def lower_raw(name) do
String.downcase(name, :ascii)
end

@spec lower_raws([raw()]) :: [raw()]
def lower_raws(headers) do
Enum.map(headers, fn {name, value} -> {lower_raw(name), value} end)
end
end
65 changes: 0 additions & 65 deletions lib/mint/core/util.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,6 @@ defmodule Mint.Core.Util do

alias Mint.Types

@unallowed_trailer_headers MapSet.new([
"content-encoding",
"content-length",
"content-range",
"content-type",
"trailer",
"transfer-encoding",

# Control headers (https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7231.html#rfc.section.5.1)
"cache-control",
"expect",
"host",
"max-forwards",
"pragma",
"range",
"te",

# Conditionals (https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7231.html#rfc.section.5.2)
"if-match",
"if-none-match",
"if-modified-since",
"if-unmodified-since",
"if-range",

# Authentication/authorization (https://tools.ietf.org/html/rfc7235#section-5.3)
"authorization",
"proxy-authenticate",
"proxy-authorization",
"www-authenticate",

# Cookie management (https://tools.ietf.org/html/rfc6265)
"cookie",
"set-cookie",

# Control data (https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7231.html#rfc.section.7.1)
"age",
"cache-control",
"expires",
"date",
"location",
"retry-after",
"vary",
"warning"
])

@spec hostname(keyword(), String.t()) :: String.t()
def hostname(opts, address) when is_list(opts) do
case Keyword.fetch(opts, :hostname) do
Expand Down Expand Up @@ -113,24 +68,4 @@ defmodule Mint.Core.Util do
@spec maybe_concat(binary(), binary()) :: binary()
def maybe_concat(<<>>, data), do: data
def maybe_concat(buffer, data) when is_binary(buffer), do: buffer <> data

@spec lower_header_name(String.t()) :: String.t()
def lower_header_name(name) do
String.downcase(name, :ascii)
end

@spec lower_header_keys(Types.headers()) :: Types.headers()
def lower_header_keys(headers) do
:lists.map(fn {name, value} -> {lower_header_name(name), value} end, headers)
end

@spec find_unallowed_trailer_header(Types.headers()) :: {String.t(), String.t()} | nil
def find_unallowed_trailer_header(headers) do
Enum.find(headers, fn {name, _value} -> name in @unallowed_trailer_headers end)
end

@spec remove_unallowed_trailer_headers(Types.headers()) :: Types.headers()
def remove_unallowed_trailer_headers(headers) do
Enum.reject(headers, fn {name, _value} -> name in @unallowed_trailer_headers end)
end
end
6 changes: 2 additions & 4 deletions lib/mint/http.ex
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,8 @@ defmodule Mint.HTTP do
> gets logged by using the `Logger` API and Erlang's `:logger` module.
"""

import Mint.Core.Util

alias Mint.{Types, TunnelProxy, UnsafeProxy}
alias Mint.Core.Transport
alias Mint.Core.{Transport, Util}

@behaviour Mint.Core.Conn

Expand Down Expand Up @@ -410,7 +408,7 @@ defmodule Mint.HTTP do
def connect(scheme, address, port, opts \\ []) do
case Keyword.fetch(opts, :proxy) do
{:ok, {proxy_scheme, proxy_address, proxy_port, proxy_opts}} ->
case scheme_to_transport(scheme) do
case Util.scheme_to_transport(scheme) do
Transport.TCP ->
proxy = {proxy_scheme, proxy_address, proxy_port}
host = {scheme, address, port}
Expand Down
Loading

0 comments on commit 74e0ec6

Please sign in to comment.