Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add :downcase_request_headers option to HTTP1.connect #399

Merged
merged 12 commits into from
Feb 12, 2024
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
Loading