Skip to content

Commit 74e0ec6

Browse files
Add :downcase_request_headers option to HTTP1.connect (#399)
1 parent ce8102b commit 74e0ec6

File tree

9 files changed

+321
-121
lines changed

9 files changed

+321
-121
lines changed

lib/mint/core/headers.ex

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
defmodule Mint.Core.Headers do
2+
@moduledoc false
3+
4+
@type canonical() ::
5+
{original_name :: String.t(), canonical_name :: String.t(), value :: String.t()}
6+
@type raw() :: {original_name :: String.t(), value :: String.t()}
7+
8+
@unallowed_trailers MapSet.new([
9+
"content-encoding",
10+
"content-length",
11+
"content-range",
12+
"content-type",
13+
"trailer",
14+
"transfer-encoding",
15+
16+
# Control headers (https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7231.html#rfc.section.5.1)
17+
"cache-control",
18+
"expect",
19+
"host",
20+
"max-forwards",
21+
"pragma",
22+
"range",
23+
"te",
24+
25+
# Conditionals (https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7231.html#rfc.section.5.2)
26+
"if-match",
27+
"if-none-match",
28+
"if-modified-since",
29+
"if-unmodified-since",
30+
"if-range",
31+
32+
# Authentication/authorization (https://tools.ietf.org/html/rfc7235#section-5.3)
33+
"authorization",
34+
"proxy-authenticate",
35+
"proxy-authorization",
36+
"www-authenticate",
37+
38+
# Cookie management (https://tools.ietf.org/html/rfc6265)
39+
"cookie",
40+
"set-cookie",
41+
42+
# Control data (https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7231.html#rfc.section.7.1)
43+
"age",
44+
"cache-control",
45+
"expires",
46+
"date",
47+
"location",
48+
"retry-after",
49+
"vary",
50+
"warning"
51+
])
52+
53+
@spec from_raw([raw()]) :: [canonical()]
54+
def from_raw(headers) do
55+
Enum.map(headers, fn {name, value} -> {name, lower_raw(name), value} end)
56+
end
57+
58+
@spec to_raw([canonical()], boolean()) :: [raw()]
59+
def to_raw(headers, _case_sensitive = true) do
60+
Enum.map(headers, fn {name, _canonical_name, value} -> {name, value} end)
61+
end
62+
63+
def to_raw(headers, _case_sensitive = false) do
64+
Enum.map(headers, fn {_name, canonical_name, value} ->
65+
{canonical_name, value}
66+
end)
67+
end
68+
69+
@spec find([canonical()], String.t()) :: {String.t(), String.t()} | nil
70+
def find(headers, name) do
71+
case List.keyfind(headers, name, 1) do
72+
nil -> nil
73+
{name, _canonical_name, value} -> {name, value}
74+
end
75+
end
76+
77+
@spec replace([canonical()], String.t(), String.t(), String.t()) ::
78+
[canonical()]
79+
def replace(headers, new_name, canonical_name, value) do
80+
List.keyreplace(headers, canonical_name, 1, {new_name, canonical_name, value})
81+
end
82+
83+
@spec has?([canonical()], String.t()) :: boolean()
84+
def has?(headers, name) do
85+
List.keymember?(headers, name, 1)
86+
end
87+
88+
@spec put_new([canonical()], String.t(), String.t(), String.t() | nil) ::
89+
[canonical()]
90+
def put_new(headers, _name, _canonical_name, nil) do
91+
headers
92+
end
93+
94+
def put_new(headers, name, canonical_name, value) do
95+
if List.keymember?(headers, canonical_name, 1) do
96+
headers
97+
else
98+
[{name, canonical_name, value} | headers]
99+
end
100+
end
101+
102+
@spec put_new([canonical()], String.t(), String.t(), (-> String.t())) ::
103+
[canonical()]
104+
def put_new_lazy(headers, name, canonical_name, fun) do
105+
if List.keymember?(headers, canonical_name, 1) do
106+
headers
107+
else
108+
[{name, canonical_name, fun.()} | headers]
109+
end
110+
end
111+
112+
@spec find_unallowed_trailer([canonical()]) :: String.t() | nil
113+
def find_unallowed_trailer(headers) do
114+
Enum.find_value(headers, fn
115+
{raw_name, canonical_name, _value} ->
116+
if canonical_name in @unallowed_trailers do
117+
raw_name
118+
end
119+
end)
120+
end
121+
122+
@spec remove_unallowed_trailer([raw()]) :: [raw()]
123+
def remove_unallowed_trailer(headers) do
124+
Enum.reject(headers, fn {name, _value} -> name in @unallowed_trailers end)
125+
end
126+
127+
@spec lower_raw(String.t()) :: String.t()
128+
def lower_raw(name) do
129+
String.downcase(name, :ascii)
130+
end
131+
132+
@spec lower_raws([raw()]) :: [raw()]
133+
def lower_raws(headers) do
134+
Enum.map(headers, fn {name, value} -> {lower_raw(name), value} end)
135+
end
136+
end

lib/mint/core/util.ex

Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -3,51 +3,6 @@ defmodule Mint.Core.Util do
33

44
alias Mint.Types
55

6-
@unallowed_trailer_headers MapSet.new([
7-
"content-encoding",
8-
"content-length",
9-
"content-range",
10-
"content-type",
11-
"trailer",
12-
"transfer-encoding",
13-
14-
# Control headers (https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7231.html#rfc.section.5.1)
15-
"cache-control",
16-
"expect",
17-
"host",
18-
"max-forwards",
19-
"pragma",
20-
"range",
21-
"te",
22-
23-
# Conditionals (https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7231.html#rfc.section.5.2)
24-
"if-match",
25-
"if-none-match",
26-
"if-modified-since",
27-
"if-unmodified-since",
28-
"if-range",
29-
30-
# Authentication/authorization (https://tools.ietf.org/html/rfc7235#section-5.3)
31-
"authorization",
32-
"proxy-authenticate",
33-
"proxy-authorization",
34-
"www-authenticate",
35-
36-
# Cookie management (https://tools.ietf.org/html/rfc6265)
37-
"cookie",
38-
"set-cookie",
39-
40-
# Control data (https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7231.html#rfc.section.7.1)
41-
"age",
42-
"cache-control",
43-
"expires",
44-
"date",
45-
"location",
46-
"retry-after",
47-
"vary",
48-
"warning"
49-
])
50-
516
@spec hostname(keyword(), String.t()) :: String.t()
527
def hostname(opts, address) when is_list(opts) do
538
case Keyword.fetch(opts, :hostname) do
@@ -113,24 +68,4 @@ defmodule Mint.Core.Util do
11368
@spec maybe_concat(binary(), binary()) :: binary()
11469
def maybe_concat(<<>>, data), do: data
11570
def maybe_concat(buffer, data) when is_binary(buffer), do: buffer <> data
116-
117-
@spec lower_header_name(String.t()) :: String.t()
118-
def lower_header_name(name) do
119-
String.downcase(name, :ascii)
120-
end
121-
122-
@spec lower_header_keys(Types.headers()) :: Types.headers()
123-
def lower_header_keys(headers) do
124-
:lists.map(fn {name, value} -> {lower_header_name(name), value} end, headers)
125-
end
126-
127-
@spec find_unallowed_trailer_header(Types.headers()) :: {String.t(), String.t()} | nil
128-
def find_unallowed_trailer_header(headers) do
129-
Enum.find(headers, fn {name, _value} -> name in @unallowed_trailer_headers end)
130-
end
131-
132-
@spec remove_unallowed_trailer_headers(Types.headers()) :: Types.headers()
133-
def remove_unallowed_trailer_headers(headers) do
134-
Enum.reject(headers, fn {name, _value} -> name in @unallowed_trailer_headers end)
135-
end
13671
end

lib/mint/http.ex

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,8 @@ defmodule Mint.HTTP do
121121
> gets logged by using the `Logger` API and Erlang's `:logger` module.
122122
"""
123123

124-
import Mint.Core.Util
125-
126124
alias Mint.{Types, TunnelProxy, UnsafeProxy}
127-
alias Mint.Core.Transport
125+
alias Mint.Core.{Transport, Util}
128126

129127
@behaviour Mint.Core.Conn
130128

@@ -410,7 +408,7 @@ defmodule Mint.HTTP do
410408
def connect(scheme, address, port, opts \\ []) do
411409
case Keyword.fetch(opts, :proxy) do
412410
{:ok, {proxy_scheme, proxy_address, proxy_port, proxy_opts}} ->
413-
case scheme_to_transport(scheme) do
411+
case Util.scheme_to_transport(scheme) do
414412
Transport.TCP ->
415413
proxy = {proxy_scheme, proxy_address, proxy_port}
416414
host = {scheme, address, port}

0 commit comments

Comments
 (0)