changed CHANGELOG.md
 
@@ -1,5 +1,22 @@
1
1
# Changelog
2
2
3
+ ## v1.6.0
4
+
5
+ ### New features
6
+
7
+ * Add `:case_sensitive_headers` option to `Mint.HTTP1.connect/4`.
8
+ * Add `:inet4` option to `Mint.HTTP.connect/4`.
9
+
10
+ ### Bug fixes and improvements
11
+
12
+ * Require Elixir 1.11+.
13
+ * Add `match_fun` clause to deal with IP addresses in TLS handshake.
14
+ * Optimize creation of HTTP/2 requests.
15
+ * Fix a compilation warning (unused `set_flag/2` function).
16
+ * Improve performance of downcasing headers.
17
+ * Deprecate `:read_write` option in `Mint.HTTP.open?/2`.
18
+ * Improve performance of checking for the CAStore library.
19
+
3
20
## v1.5.2
4
21
5
22
### Bug fixes and improvements
changed hex_metadata.config
 
@@ -1,8 +1,8 @@
1
1
{<<"links">>,[{<<"GitHub">>,<<"https://github.com/elixir-mint/mint">>}]}.
2
2
{<<"name">>,<<"mint">>}.
3
- {<<"version">>,<<"1.5.2">>}.
3
+ {<<"version">>,<<"1.6.0">>}.
4
4
{<<"description">>,<<"Small and composable HTTP client.">>}.
5
- {<<"elixir">>,<<"~> 1.10">>}.
5
+ {<<"elixir">>,<<"~> 1.11">>}.
6
6
{<<"app">>,<<"mint">>}.
7
7
{<<"licenses">>,[<<"Apache-2.0">>]}.
8
8
{<<"requirements">>,
 
@@ -14,7 +14,7 @@
14
14
[{<<"name">>,<<"hpax">>},
15
15
{<<"app">>,<<"hpax">>},
16
16
{<<"optional">>,false},
17
- {<<"requirement">>,<<"~> 0.1.1">>},
17
+ {<<"requirement">>,<<"~> 0.1.1 or ~> 0.2.0">>},
18
18
{<<"repository">>,<<"hexpm">>}]]}.
19
19
{<<"files">>,
20
20
[<<"lib">>,<<"lib/mint">>,<<"lib/mint/http1">>,
 
@@ -23,8 +23,9 @@
23
23
<<"lib/mint/tunnel_proxy.ex">>,<<"lib/mint/core">>,
24
24
<<"lib/mint/core/util.ex">>,<<"lib/mint/core/transport">>,
25
25
<<"lib/mint/core/transport/ssl.ex">>,<<"lib/mint/core/transport/tcp.ex">>,
26
- <<"lib/mint/core/transport.ex">>,<<"lib/mint/core/conn.ex">>,
27
- <<"lib/mint/types.ex">>,<<"lib/mint/http_error.ex">>,<<"lib/mint/http2">>,
26
+ <<"lib/mint/core/transport.ex">>,<<"lib/mint/core/headers.ex">>,
27
+ <<"lib/mint/core/conn.ex">>,<<"lib/mint/types.ex">>,
28
+ <<"lib/mint/http_error.ex">>,<<"lib/mint/http2">>,
28
29
<<"lib/mint/http2/frame.ex">>,<<"lib/mint/negotiate.ex">>,
29
30
<<"lib/mint/http1.ex">>,<<"lib/mint/application.ex">>,
30
31
<<"lib/mint/http.ex">>,<<"lib/mint/unsafe_proxy.ex">>,
changed lib/mint/core/conn.ex
 
@@ -13,7 +13,7 @@ defmodule Mint.Core.Conn do
13
13
keyword()
14
14
) :: {:ok, conn()} | {:error, Types.error()}
15
15
16
- @callback open?(conn(), :read | :write | :read_write) :: boolean()
16
+ @callback open?(conn(), :read | :write) :: boolean()
17
17
18
18
@callback close(conn()) :: {:ok, conn()}
added lib/mint/core/headers.ex
 
@@ -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
changed lib/mint/core/transport/ssl.ex
 
@@ -323,6 +323,7 @@ defmodule Mint.Core.Transport.SSL do
323
323
324
324
defp connect(address, hostname, port, opts) do
325
325
timeout = Keyword.get(opts, :timeout, @default_timeout)
326
+ inet4? = Keyword.get(opts, :inet4, true)
326
327
inet6? = Keyword.get(opts, :inet6, false)
327
328
328
329
opts = ssl_opts(String.to_charlist(hostname), opts)
 
@@ -334,8 +335,11 @@ defmodule Mint.Core.Transport.SSL do
334
335
{:ok, sslsocket} ->
335
336
{:ok, sslsocket}
336
337
337
- _error ->
338
+ _error when inet4? ->
338
339
wrap_err(:ssl.connect(address, port, opts, timeout))
340
+
341
+ error ->
342
+ wrap_err(error)
339
343
end
340
344
else
341
345
# Use the defaults provided by ssl/gen_tcp.
 
@@ -428,7 +432,7 @@ defmodule Mint.Core.Transport.SSL do
428
432
default_ssl_opts(hostname)
429
433
|> Keyword.merge(opts)
430
434
|> Keyword.merge(@transport_opts)
431
- |> Keyword.drop([:timeout, :inet6])
435
+ |> Keyword.drop([:timeout, :inet4, :inet6])
432
436
|> add_verify_opts(hostname)
433
437
|> remove_incompatible_ssl_opts()
434
438
|> add_ciphers_opt()
 
@@ -509,6 +513,20 @@ defmodule Mint.Core.Transport.SSL do
509
513
end
510
514
end
511
515
516
+ # Workaround for a bug that was fixed in OTP 27:
517
+ # Before OTP 27 when connecting to an IP address and the server offers a
518
+ # certificate with its IP address in the "subject alternate names" extension,
519
+ # the TLS handshake fails with a `{:bad_cert, :hostname_check_failed}`.
520
+ # This clause can be removed once we depend on OTP 27+.
521
+ defp match_fun({:dns_id, hostname}, {:iPAddress, ip}) do
522
+ with {:ok, ip_tuple} <- :inet.parse_address(hostname),
523
+ ^ip <- Tuple.to_list(ip_tuple) do
524
+ true
525
+ else
526
+ _ -> :default
527
+ end
528
+ end
529
+
512
530
defp match_fun(_reference, _presented), do: :default
513
531
514
532
defp domain_without_host([]), do: []
 
@@ -684,8 +702,12 @@ defmodule Mint.Core.Transport.SSL do
684
702
defp blocked_cipher?({kex, cipher, _mac, prf}), do: blocked_cipher?({kex, cipher, prf})
685
703
defp blocked_cipher?({_kex, _cipher, _prf} = suite), do: suite in @blocked_ciphers
686
704
687
- defp raise_on_missing_castore! do
688
- Code.ensure_loaded?(CAStore) ||
705
+ if Code.ensure_loaded?(CAStore) do
706
+ defp raise_on_missing_castore! do
707
+ :ok
708
+ end
709
+ else
710
+ defp raise_on_missing_castore! do
689
711
raise """
690
712
default CA trust store not available; please add `:castore` to your project's \
691
713
dependencies or specify the trust store using the :cacertfile/:cacerts option \
 
@@ -696,6 +718,7 @@ defmodule Mint.Core.Transport.SSL do
696
718
697
719
See: https://www.erlang.org/blog/my-otp-25-highlights/#ca-certificates-can-be-fetched-from-the-os-standard-place
698
720
"""
721
+ end
699
722
end
700
723
701
724
defp wrap_err({:error, reason}), do: {:error, wrap_error(reason)}
changed lib/mint/core/transport/tcp.ex
 
@@ -19,12 +19,13 @@ defmodule Mint.Core.Transport.TCP do
19
19
opts = Keyword.delete(opts, :hostname)
20
20
21
21
timeout = Keyword.get(opts, :timeout, @default_timeout)
22
+ inet4? = Keyword.get(opts, :inet4, true)
22
23
inet6? = Keyword.get(opts, :inet6, false)
23
24
24
25
opts =
25
26
opts
26
27
|> Keyword.merge(@transport_opts)
27
- |> Keyword.drop([:alpn_advertised_protocols, :timeout, :inet6])
28
+ |> Keyword.drop([:alpn_advertised_protocols, :timeout, :inet4, :inet6])
28
29
29
30
if inet6? do
30
31
# Try inet6 first, then fall back to the defaults provided by
 
@@ -33,8 +34,11 @@ defmodule Mint.Core.Transport.TCP do
33
34
{:ok, socket} ->
34
35
{:ok, socket}
35
36
36
- _error ->
37
+ _error when inet4? ->
37
38
wrap_err(:gen_tcp.connect(address, port, opts, timeout))
39
+
40
+ error ->
41
+ wrap_err(error)
38
42
end
39
43
else
40
44
# Use the defaults provided by gen_tcp.
changed lib/mint/core/util.ex
 
@@ -3,51 +3,6 @@ defmodule Mint.Core.Util do
3
3
4
4
alias Mint.Types
5
5
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
-
51
6
@spec hostname(keyword(), String.t()) :: String.t()
52
7
def hostname(opts, address) when is_list(opts) do
53
8
case Keyword.fetch(opts, :hostname) do
 
@@ -107,31 +62,10 @@ defmodule Mint.Core.Util do
107
62
end
108
63
end
109
64
110
- # Lowercases an ASCII string more efficiently than
111
- # String.downcase/1.
112
- @spec downcase_ascii(String.t()) :: String.t()
113
- def downcase_ascii(string) do
114
- for <<char <- string>>, do: <<downcase_ascii_char(char)>>, into: ""
115
- end
116
-
117
- @spec downcase_ascii_char(byte()) :: byte()
118
- def downcase_ascii_char(char) when char in ?A..?Z, do: char + 32
119
- def downcase_ascii_char(char) when char in 0..127, do: char
120
-
121
65
# If the buffer is empty, reusing the incoming data saves
122
66
# a potentially large allocation of memory.
123
67
# This should be fixed in a subsequent OTP release.
124
68
@spec maybe_concat(binary(), binary()) :: binary()
125
69
def maybe_concat(<<>>, data), do: data
126
70
def maybe_concat(buffer, data) when is_binary(buffer), do: buffer <> data
127
-
128
- @spec find_unallowed_trailer_header(Types.headers()) :: {String.t(), String.t()} | nil
129
- def find_unallowed_trailer_header(headers) do
130
- Enum.find(headers, fn {name, _value} -> name in @unallowed_trailer_headers end)
131
- end
132
-
133
- @spec remove_unallowed_trailer_headers(Types.headers()) :: Types.headers()
134
- def remove_unallowed_trailer_headers(headers) do
135
- Enum.reject(headers, fn {name, _value} -> name in @unallowed_trailer_headers end)
136
- end
137
71
end
changed lib/mint/http.ex
 
@@ -121,22 +121,13 @@ defmodule Mint.HTTP do
121
121
> gets logged by using the `Logger` API and Erlang's `:logger` module.
122
122
"""
123
123
124
- import Mint.Core.Util
125
-
126
124
alias Mint.{Types, TunnelProxy, UnsafeProxy}
127
- alias Mint.Core.Transport
125
+ alias Mint.Core.{Transport, Util}
128
126
129
127
@behaviour Mint.Core.Conn
130
128
131
129
@opaque t() :: Mint.HTTP1.t() | Mint.HTTP2.t()
132
130
133
- # TODO: Remove once we depend on Elixir 1.11+, which defines is_struct/2
134
- if not macro_exported?(Kernel, :is_struct, 2) do
135
- defguardp is_struct(struct, module)
136
- when is_map(struct) and is_atom(module) and is_map_key(struct, :__struct__) and
137
- :erlang.map_get(:__struct__, struct) == module
138
- end
139
-
140
131
defguardp is_data_message(message)
141
132
when elem(message, 0) in [:ssl, :tcp] and tuple_size(message) == 3
142
133
 
@@ -301,6 +292,13 @@ defmodule Mint.HTTP do
301
292
seconds), and may be overridden by the caller. Set to `:infinity` to
302
293
disable the connect timeout.
303
294
295
+ * `:inet6` - if set to `true` enables IPv6 connection. Defaults to `false`
296
+ and may be overridden by the caller.
297
+
298
+ * `:inet4` - if set to `true` falls back to IPv4 if IPv6 connection fails.
299
+ Defaults to `true` and may be overridden by the caller. *Available since
300
+ v1.6.0*.
301
+
304
302
Options for `:https` only:
305
303
306
304
* `:alpn_advertised_protocols` - managed by Mint. Cannot be overridden.
 
@@ -410,7 +408,7 @@ defmodule Mint.HTTP do
410
408
def connect(scheme, address, port, opts \\ []) do
411
409
case Keyword.fetch(opts, :proxy) do
412
410
{:ok, {proxy_scheme, proxy_address, proxy_port, proxy_opts}} ->
413
- case scheme_to_transport(scheme) do
411
+ case Util.scheme_to_transport(scheme) do
414
412
Transport.TCP ->
415
413
proxy = {proxy_scheme, proxy_address, proxy_port}
416
414
host = {scheme, address, port}
 
@@ -505,16 +503,16 @@ defmodule Mint.HTTP do
505
503
either open, or closed (for both reading and writing). In HTTP/2, the connection can be closed only
506
504
for writing but not for reading, meaning that you cannot send any more data to the
507
505
server but you can still receive data from the server. In this case, `Mint.HTTP.open?(conn, :read)`
508
- would return `true` but `Mint.HTTP.open?(conn, :read_write)` would return `false`.
506
+ would return `true` but `Mint.HTTP.open?(conn, :write)` would return `false`.
509
507
See the "Closed connection" section in the module documentation of `Mint.HTTP2`.
510
508
511
509
If a connection is *completely closed* (that is, `Mint.HTTP.open?(conn, :read)` returns `false`),
512
510
it has become useless and you should get rid of it. If you still need a connection
513
511
to the server, start a new connection with `connect/4`.
514
512
515
- > #### The default value of `type` is `:read_write` {: .warning}
513
+ > #### The default value of `type` is `:write` {: .warning}
516
514
>
517
- > With the default value of `type` being `:read_write`, a call to
515
+ > With the default value of `type` being `:write`, a call to
518
516
> `Mint.HTTP.open?(conn)` will return `false` if `conn` was closed for writing
519
517
> but is still open for reading. If you need to make sure the connection is
520
518
> completely closed, check that `Mint.HTTP.open?(conn, :read)` returns `false`.
 
@@ -527,8 +525,8 @@ defmodule Mint.HTTP do
527
525
528
526
"""
529
527
@impl true
530
- @spec open?(t(), :read | :write | :read_write) :: boolean()
531
- def open?(conn, type \\ :read_write), do: conn_module(conn).open?(conn, type)
528
+ @spec open?(t(), :read | :write) :: boolean()
529
+ def open?(conn, type \\ :write), do: conn_module(conn).open?(conn, type)
532
530
533
531
@doc """
534
532
Sends a request to the connected server.
changed lib/mint/http1.ex
 
@@ -13,9 +13,8 @@ defmodule Mint.HTTP1 do
13
13
how to use the data structure and client architecture, see `Mint`.
14
14
"""
15
15
16
- import Mint.Core.Util
16
+ alias Mint.Core.{Headers, Util}
17
17
18
- alias Mint.Core.Util
19
18
alias Mint.HTTP1.{Parse, Request, Response}
20
19
alias Mint.{HTTPError, TransportError, Types}
21
20
 
@@ -93,6 +92,7 @@ defmodule Mint.HTTP1 do
93
92
:transport,
94
93
:mode,
95
94
:scheme_as_string,
95
+ :case_sensitive_headers,
96
96
requests: :queue.new(),
97
97
state: :closed,
98
98
buffer: "",
 
@@ -117,6 +117,13 @@ defmodule Mint.HTTP1 do
117
117
Same as `Mint.HTTP.connect/4`, but forces an HTTP/1 or HTTP/1.1 connection.
118
118
119
119
This function doesn't support proxying.
120
+
121
+ ## Additional Options
122
+
123
+ * `:case_sensitive_headers` - (boolean) if set to `true` the case of the supplied
124
+ headers in requests will be preserved. The default is to lowercase the headers
125
+ because HTTP/1.1 header names are case-insensitive. *Available since v1.6.0*.
126
+
120
127
"""
121
128
@spec connect(Types.scheme(), Types.address(), :inet.port_number(), keyword()) ::
122
129
{:ok, t()} | {:error, Types.error()}
 
@@ -124,7 +131,7 @@ defmodule Mint.HTTP1 do
124
131
# TODO: Also ALPN negotiate HTTP1?
125
132
126
133
hostname = Mint.Core.Util.hostname(opts, address)
127
- transport = scheme_to_transport(scheme)
134
+ transport = Util.scheme_to_transport(scheme)
128
135
129
136
transport_opts =
130
137
Keyword.get(opts, :transport_opts, [])
 
@@ -147,7 +154,7 @@ defmodule Mint.HTTP1 do
147
154
def upgrade(old_scheme, socket, new_scheme, hostname, port, opts) do
148
155
# TODO: Also ALPN negotiate HTTP1?
149
156
150
- transport = scheme_to_transport(new_scheme)
157
+ transport = Util.scheme_to_transport(new_scheme)
151
158
152
159
transport_opts =
153
160
Keyword.get(opts, :transport_opts, [])
 
@@ -168,7 +175,7 @@ defmodule Mint.HTTP1 do
168
175
keyword()
169
176
) :: {:ok, t()} | {:error, Types.error()}
170
177
def initiate(scheme, socket, hostname, port, opts) do
171
- transport = scheme_to_transport(scheme)
178
+ transport = Util.scheme_to_transport(scheme)
172
179
mode = Keyword.get(opts, :mode, :active)
173
180
log? = Keyword.get(opts, :log, false)
174
181
 
@@ -182,7 +189,7 @@ defmodule Mint.HTTP1 do
182
189
"the :log option must be a boolean, got: #{inspect(log?)}"
183
190
end
184
191
185
- with :ok <- inet_opts(transport, socket),
192
+ with :ok <- Util.inet_opts(transport, socket),
186
193
:ok <- if(mode == :active, do: transport.setopts(socket, active: :once), else: :ok) do
187
194
conn = %__MODULE__{
188
195
transport: transport,
 
@@ -192,7 +199,8 @@ defmodule Mint.HTTP1 do
192
199
port: port,
193
200
scheme_as_string: Atom.to_string(scheme),
194
201
state: :open,
195
- log: log?
202
+ log: log?,
203
+ case_sensitive_headers: Keyword.get(opts, :case_sensitive_headers, false)
196
204
}
197
205
198
206
{:ok, conn}
 
@@ -223,9 +231,10 @@ defmodule Mint.HTTP1 do
223
231
See `Mint.HTTP.open?/1`.
224
232
"""
225
233
@impl true
226
- @spec open?(t(), :read | :write | :read_write) :: boolean()
227
- def open?(conn, type \\ :read_write)
234
+ @spec open?(t(), :read | :write) :: boolean()
235
+ def open?(conn, type \\ :write)
228
236
237
+ # TODO: hard-deprecate :read_write in 1.7.
229
238
def open?(%__MODULE__{state: state}, type) when type in [:read, :write, :read_write] do
230
239
state == :open
231
240
end
 
@@ -262,11 +271,17 @@ defmodule Mint.HTTP1 do
262
271
263
272
headers =
264
273
headers
265
- |> lower_header_keys()
274
+ |> Headers.from_raw()
266
275
|> add_default_headers(conn)
267
276
268
277
with {:ok, headers, encoding} <- add_content_length_or_transfer_encoding(headers, body),
269
- {:ok, iodata} <- Request.encode(method, path, headers, body),
278
+ {:ok, iodata} <-
279
+ Request.encode(
280
+ method,
281
+ path,
282
+ Headers.to_raw(headers, conn.case_sensitive_headers),
283
+ body
284
+ ),
270
285
:ok <- transport.send(socket, iodata) do
271
286
request_ref = make_ref()
272
287
request = new_request(request_ref, method, body, encoding)
 
@@ -363,7 +378,7 @@ defmodule Mint.HTTP1 do
363
378
ref,
364
379
chunk
365
380
) do
366
- with {:ok, chunk} <- validate_chunk(chunk),
381
+ with {:ok, chunk} <- validate_chunk(conn, chunk),
367
382
:ok <- conn.transport.send(conn.socket, Request.encode_chunk(chunk)) do
368
383
case chunk do
369
384
:eof ->
 
@@ -391,21 +406,21 @@ defmodule Mint.HTTP1 do
391
406
end
392
407
end
393
408
394
- defp validate_chunk({:eof, trailer_headers}) do
395
- headers = lower_header_keys(trailer_headers)
409
+ defp validate_chunk(conn, {:eof, trailers}) do
410
+ trailers = Headers.from_raw(trailers)
396
411
397
- if unallowed_header = find_unallowed_trailer_header(headers) do
412
+ if unallowed_header = Headers.find_unallowed_trailer(trailers) do
398
413
{:error, wrap_error({:unallowed_trailing_header, unallowed_header})}
399
414
else
400
- {:ok, {:eof, headers}}
415
+ {:ok, {:eof, Headers.to_raw(trailers, conn.case_sensitive_headers)}}
401
416
end
402
417
end
403
418
404
- defp validate_chunk(:eof) do
419
+ defp validate_chunk(_conn, :eof) do
405
420
{:ok, :eof}
406
421
end
407
422
408
- defp validate_chunk(chunk) do
423
+ defp validate_chunk(_conn, chunk) do
409
424
if IO.iodata_length(chunk) == 0 do
410
425
:empty_chunk
411
426
else
 
@@ -458,7 +473,7 @@ defmodule Mint.HTTP1 do
458
473
end
459
474
460
475
defp handle_data(%__MODULE__{request: request} = conn, data) do
461
- data = maybe_concat(conn.buffer, data)
476
+ data = Util.maybe_concat(conn.buffer, data)
462
477
463
478
case decode(request.state, conn, data, []) do
464
479
{:ok, conn, responses} ->
 
@@ -795,7 +810,7 @@ defmodule Mint.HTTP1 do
795
810
decode_trailer_headers(conn, rest, responses, headers)
796
811
797
812
{:ok, :eof, rest} ->
798
- headers = Util.remove_unallowed_trailer_headers(headers)
813
+ headers = Headers.remove_unallowed_trailer(headers)
799
814
800
815
responses = [
801
816
{:done, conn.request.ref}
 
@@ -972,14 +987,10 @@ defmodule Mint.HTTP1 do
972
987
}
973
988
end
974
989
975
- defp lower_header_keys(headers) do
976
- for {name, value} <- headers, do: {Util.downcase_ascii(name), value}
977
- end
978
-
979
990
defp add_default_headers(headers, conn) do
980
991
headers
981
- |> Util.put_new_header("user-agent", @user_agent)
982
- |> Util.put_new_header("host", default_host_header(conn))
992
+ |> Headers.put_new("User-Agent", "user-agent", @user_agent)
993
+ |> Headers.put_new("Host", "host", default_host_header(conn))
983
994
end
984
995
985
996
# If the port is the default for the scheme, don't add it to the host header
 
@@ -993,18 +1004,19 @@ defmodule Mint.HTTP1 do
993
1004
994
1005
defp add_content_length_or_transfer_encoding(headers, :stream) do
995
1006
cond do
996
- List.keymember?(headers, "content-length", 0) ->
1007
+ Headers.has?(headers, "content-length") ->
997
1008
{:ok, headers, :identity}
998
1009
999
- found = List.keyfind(headers, "transfer-encoding", 0) ->
1000
- {"transfer-encoding", value} = found
1010
+ found = Headers.find(headers, "transfer-encoding") ->
1011
+ {raw_name, value} = found
1001
1012
1002
1013
with {:ok, tokens} <- Parse.transfer_encoding_header(value) do
1003
1014
if "chunked" in tokens or "identity" in tokens do
1004
1015
{:ok, headers, :identity}
1005
1016
else
1006
- new_transfer_encoding = {"transfer-encoding", value <> ",chunked"}
1007
- headers = List.keyreplace(headers, "transfer-encoding", 0, new_transfer_encoding)
1017
+ headers =
1018
+ Headers.replace(headers, raw_name, "transfer-encoding", value <> ",chunked")
1019
+
1008
1020
{:ok, headers, :chunked}
1009
1021
end
1010
1022
end
 
@@ -1012,7 +1024,9 @@ defmodule Mint.HTTP1 do
1012
1024
# If no content-length or transfer-encoding are present, assume
1013
1025
# chunked transfer-encoding and handle the encoding ourselves.
1014
1026
true ->
1015
- headers = Util.put_new_header(headers, "transfer-encoding", "chunked")
1027
+ headers =
1028
+ Headers.put_new(headers, "Transfer-Encoding", "transfer-encoding", "chunked")
1029
+
1016
1030
{:ok, headers, :chunked}
1017
1031
end
1018
1032
end
 
@@ -1023,7 +1037,9 @@ defmodule Mint.HTTP1 do
1023
1037
1024
1038
defp add_content_length_or_transfer_encoding(headers, body) do
1025
1039
length_fun = fn -> body |> IO.iodata_length() |> Integer.to_string() end
1026
- {:ok, Util.put_new_header_lazy(headers, "content-length", length_fun), :identity}
1040
+
1041
+ {:ok, Headers.put_new_lazy(headers, "Content-Length", "content-length", length_fun),
1042
+ :identity}
1027
1043
end
1028
1044
1029
1045
defp wrap_error(reason) do
changed lib/mint/http1/parse.ex
 
@@ -1,8 +1,6 @@
1
1
defmodule Mint.HTTP1.Parse do
2
2
@moduledoc false
3
3
4
- alias Mint.Core.Util
5
-
6
4
defmacro is_digit(char), do: quote(do: unquote(char) in ?0..?9)
7
5
defmacro is_alpha(char), do: quote(do: unquote(char) in ?a..?z or unquote(char) in ?A..?Z)
8
6
defmacro is_whitespace(char), do: quote(do: unquote(char) in ~c"\s\t")
 
@@ -55,7 +53,7 @@ defmodule Mint.HTTP1.Parse do
55
53
defp token_list_downcase(rest, acc), do: token_downcase(rest, _token_acc = <<>>, acc)
56
54
57
55
defp token_downcase(<<char, rest::binary>>, token_acc, acc) when is_tchar(char),
58
- do: token_downcase(rest, <<token_acc::binary, Util.downcase_ascii_char(char)>>, acc)
56
+ do: token_downcase(rest, <<token_acc::binary, downcase_ascii_char(char)>>, acc)
59
57
60
58
defp token_downcase(rest, token_acc, acc), do: token_list_sep_downcase(rest, [token_acc | acc])
61
59
 
@@ -68,4 +66,7 @@ defmodule Mint.HTTP1.Parse do
68
66
do: token_list_downcase(rest, acc)
69
67
70
68
defp token_list_sep_downcase(_rest, _acc), do: :error
69
+
70
+ defp downcase_ascii_char(char) when char in ?A..?Z, do: char + 32
71
+ defp downcase_ascii_char(char) when char in 0..127, do: char
71
72
end
changed lib/mint/http1/response.ex
 
@@ -1,7 +1,7 @@
1
1
defmodule Mint.HTTP1.Response do
2
2
@moduledoc false
3
3
4
- alias Mint.Core.Util
4
+ alias Mint.Core.Headers
5
5
6
6
def decode_status_line(binary) do
7
7
case :erlang.decode_packet(:http_bin, binary, []) do
 
@@ -38,6 +38,6 @@ defmodule Mint.HTTP1.Response do
38
38
end
39
39
end
40
40
41
- defp header_name(atom) when is_atom(atom), do: atom |> Atom.to_string() |> Util.downcase_ascii()
42
- defp header_name(binary) when is_binary(binary), do: Util.downcase_ascii(binary)
41
+ defp header_name(atom) when is_atom(atom), do: atom |> Atom.to_string() |> header_name()
42
+ defp header_name(binary) when is_binary(binary), do: Headers.lower_raw(binary)
43
43
end
changed lib/mint/http2.ex
 
@@ -125,12 +125,11 @@ defmodule Mint.HTTP2 do
125
125
> is enabled.
126
126
"""
127
127
128
- import Mint.Core.Util
129
128
import Mint.HTTP2.Frame, except: [encode: 1, decode_next: 1, inspect: 1]
130
129
131
130
alias Mint.{HTTPError, TransportError}
132
131
alias Mint.Types
133
- alias Mint.Core.Util
132
+ alias Mint.Core.{Headers, Util}
134
133
alias Mint.HTTP2.Frame
135
134
136
135
require Logger
 
@@ -170,6 +169,7 @@ defmodule Mint.HTTP2 do
170
169
:hostname,
171
170
:port,
172
171
:scheme,
172
+ :authority,
173
173
174
174
# Connection state (open, closed, and so on).
175
175
:state,
 
@@ -232,22 +232,13 @@ defmodule Mint.HTTP2 do
232
232
conn = unquote(conn)
233
233
234
234
if conn.log do
235
- Logger.log(normalize_logger_level(unquote(level)), unquote(message))
235
+ Logger.log(unquote(level), unquote(message))
236
236
else
237
237
:ok
238
238
end
239
239
end
240
240
end
241
241
242
- # TODO: remove this once we depend on Elixir 1.11+.
243
- if macro_exported?(Logger, :warning, 2) do
244
- defp normalize_logger_level(:warning), do: :warning
245
- else
246
- defp normalize_logger_level(:warning), do: :warn
247
- end
248
-
249
- defp normalize_logger_level(level), do: level
250
-
251
242
## Types
252
243
253
244
@typedoc """
 
@@ -404,7 +395,7 @@ defmodule Mint.HTTP2 do
404
395
keyword()
405
396
) :: {:ok, t()} | {:error, Types.error()}
406
397
def upgrade(old_scheme, socket, new_scheme, hostname, port, opts) do
407
- transport = scheme_to_transport(new_scheme)
398
+ transport = Util.scheme_to_transport(new_scheme)
408
399
409
400
transport_opts =
410
401
opts
 
@@ -448,8 +439,8 @@ defmodule Mint.HTTP2 do
448
439
See `Mint.HTTP.open?/1`.
449
440
"""
450
441
@impl true
451
- @spec open?(t(), :read | :write | :read_write) :: boolean()
452
- def open?(%__MODULE__{state: state} = _conn, type \\ :read_write)
442
+ @spec open?(t(), :read | :write) :: boolean()
443
+ def open?(%__MODULE__{state: state} = _conn, type \\ :write)
453
444
when type in [:read, :write, :read_write] do
454
445
case state do
455
446
:handshaking -> true
 
@@ -520,7 +511,7 @@ defmodule Mint.HTTP2 do
520
511
when is_binary(method) and is_binary(path) and is_list(headers) do
521
512
headers =
522
513
headers
523
- |> downcase_header_names()
514
+ |> Headers.lower_raws()
524
515
|> add_pseudo_headers(conn, method, path)
525
516
|> add_default_headers(body)
526
517
|> sort_pseudo_headers_to_front()
 
@@ -973,11 +964,19 @@ defmodule Mint.HTTP2 do
973
964
keyword()
974
965
) :: {:ok, t()} | {:error, Types.error()}
975
966
def initiate(scheme, socket, hostname, port, opts) do
976
- transport = scheme_to_transport(scheme)
967
+ transport = Util.scheme_to_transport(scheme)
968
+ scheme_string = Atom.to_string(scheme)
977
969
mode = Keyword.get(opts, :mode, :active)
978
970
log? = Keyword.get(opts, :log, false)
979
971
client_settings_params = Keyword.get(opts, :client_settings, [])
980
972
validate_client_settings!(client_settings_params)
973
+ # If the port is the default for the scheme, don't add it to the :authority pseudo-header
974
+ authority =
975
+ if URI.default_port(scheme_string) == port do
976
+ hostname
977
+ else
978
+ "#{hostname}:#{port}"
979
+ end
981
980
982
981
unless mode in [:active, :passive] do
983
982
raise ArgumentError,
 
@@ -992,15 +991,16 @@ defmodule Mint.HTTP2 do
992
991
conn = %__MODULE__{
993
992
hostname: hostname,
994
993
port: port,
995
- transport: scheme_to_transport(scheme),
994
+ authority: authority,
995
+ transport: Util.scheme_to_transport(scheme),
996
996
socket: socket,
997
997
mode: mode,
998
- scheme: Atom.to_string(scheme),
998
+ scheme: scheme_string,
999
999
state: :handshaking,
1000
1000
log: log?
1001
1001
}
1002
1002
1003
- with :ok <- inet_opts(transport, socket),
1003
+ with :ok <- Util.inet_opts(transport, socket),
1004
1004
client_settings = settings(stream_id: 0, params: client_settings_params),
1005
1005
preface = [@connection_preface, Frame.encode(client_settings)],
1006
1006
:ok <- transport.send(socket, preface),
 
@@ -1055,12 +1055,12 @@ defmodule Mint.HTTP2 do
1055
1055
defp negotiate(address, port, :http, transport_opts) do
1056
1056
# We don't support protocol negotiation for TCP connections
1057
1057
# so currently we just assume the HTTP/2 protocol
1058
- transport = scheme_to_transport(:http)
1058
+ transport = Util.scheme_to_transport(:http)
1059
1059
transport.connect(address, port, transport_opts)
1060
1060
end
1061
1061
1062
1062
defp negotiate(address, port, :https, transport_opts) do
1063
- transport = scheme_to_transport(:https)
1063
+ transport = Util.scheme_to_transport(:https)
1064
1064
1065
1065
with {:ok, socket} <- transport.connect(address, port, transport_opts),
1066
1066
{:ok, protocol} <- transport.negotiated_protocol(socket) do
 
@@ -1097,14 +1097,15 @@ defmodule Mint.HTTP2 do
1097
1097
encode_data(conn, stream_id, "", [:end_stream])
1098
1098
end
1099
1099
1100
- defp encode_stream_body_request_payload(conn, stream_id, {:eof, trailer_headers}) do
1101
- lowered_headers = downcase_header_names(trailer_headers)
1100
+ defp encode_stream_body_request_payload(conn, stream_id, {:eof, trailers}) do
1101
+ trailers = Headers.from_raw(trailers)
1102
1102
1103
- if unallowed_trailer_header = Util.find_unallowed_trailer_header(lowered_headers) do
1103
+ if unallowed_trailer_header = Headers.find_unallowed_trailer(trailers) do
1104
1104
error = wrap_error({:unallowed_trailing_header, unallowed_trailer_header})
1105
1105
throw({:mint, conn, error})
1106
1106
end
1107
1107
1108
+ trailer_headers = Headers.to_raw(trailers, _case_sensitive = false)
1108
1109
encode_headers(conn, stream_id, trailer_headers, [:end_headers, :end_stream])
1109
1110
end
1110
1111
 
@@ -1334,10 +1335,6 @@ defmodule Mint.HTTP2 do
1334
1335
end)
1335
1336
end
1336
1337
1337
- defp downcase_header_names(headers) do
1338
- for {name, value} <- headers, do: {Util.downcase_ascii(name), value}
1339
- end
1340
-
1341
1338
defp add_default_headers(headers, body) do
1342
1339
headers
1343
1340
|> Util.put_new_header("user-agent", @user_agent)
 
@@ -1355,10 +1352,10 @@ defmodule Mint.HTTP2 do
1355
1352
end
1356
1353
1357
1354
defp add_pseudo_headers(headers, conn, method, path) do
1358
- if String.upcase(method) == "CONNECT" do
1355
+ if same_method?(method, "CONNECT") do
1359
1356
[
1360
1357
{":method", method},
1361
- {":authority", authority_pseudo_header(conn.scheme, conn.port, conn.hostname)}
1358
+ {":authority", conn.authority}
1362
1359
| headers
1363
1360
]
1364
1361
else
 
@@ -1366,12 +1363,27 @@ defmodule Mint.HTTP2 do
1366
1363
{":method", method},
1367
1364
{":path", path},
1368
1365
{":scheme", conn.scheme},
1369
- {":authority", authority_pseudo_header(conn.scheme, conn.port, conn.hostname)}
1366
+ {":authority", conn.authority}
1370
1367
| headers
1371
1368
]
1372
1369
end
1373
1370
end
1374
1371
1372
+ # same_method?/2 is pretty optimized, so bench before changing.
1373
+
1374
+ # Same binary, which is a common case.
1375
+ defp same_method?(bin, bin), do: true
1376
+
1377
+ # Get out early if the size is different, these can't be the same.
1378
+ defp same_method?(bin1, bin2) when byte_size(bin1) != byte_size(bin2), do: false
1379
+
1380
+ defp same_method?(<<ch, rest1::binary>>, <<ch, rest2::binary>>), do: same_method?(rest1, rest2)
1381
+
1382
+ defp same_method?(<<lower, rest1::binary>>, <<char, rest2::binary>>) when lower - 32 == char,
1383
+ do: same_method?(rest1, rest2)
1384
+
1385
+ defp same_method?(_method1, _method2), do: false
1386
+
1375
1387
defp sort_pseudo_headers_to_front(headers) do
1376
1388
Enum.sort_by(headers, fn {key, _value} ->
1377
1389
not String.starts_with?(key, ":")
 
@@ -1381,7 +1393,7 @@ defmodule Mint.HTTP2 do
1381
1393
## Frame handling
1382
1394
1383
1395
defp maybe_concat_and_handle_new_data(conn, data) do
1384
- data = maybe_concat(conn.buffer, data)
1396
+ data = Util.maybe_concat(conn.buffer, data)
1385
1397
{conn, responses} = handle_new_data(conn, data, [])
1386
1398
{:ok, conn, Enum.reverse(responses)}
1387
1399
end
 
@@ -1684,7 +1696,7 @@ defmodule Mint.HTTP2 do
1684
1696
headers when received_first_headers? ->
1685
1697
if end_stream? do
1686
1698
conn = close_stream!(conn, stream.id, :no_error)
1687
- headers = headers |> Util.remove_unallowed_trailer_headers() |> join_cookie_headers()
1699
+ headers = headers |> Headers.remove_unallowed_trailer() |> join_cookie_headers()
1688
1700
{conn, [{:done, ref}, {:headers, ref, headers} | responses]}
1689
1701
else
1690
1702
# Trailer headers must set the END_STREAM flag because they're
 
@@ -1719,18 +1731,9 @@ defmodule Mint.HTTP2 do
1719
1731
end
1720
1732
end
1721
1733
1722
- # If the port is the default for the scheme, don't add it to the :authority pseudo-header
1723
- defp authority_pseudo_header(scheme, port, hostname) do
1724
- if URI.default_port(scheme) == port do
1725
- hostname
1726
- else
1727
- "#{hostname}:#{port}"
1728
- end
1729
- end
1730
-
1731
1734
defp join_cookie_headers(headers) do
1732
1735
# If we have 0 or 1 Cookie headers, we just use the old list of headers.
1733
- case Enum.split_with(headers, fn {name, _value} -> Util.downcase_ascii(name) == "cookie" end) do
1736
+ case Enum.split_with(headers, fn {name, _value} -> Headers.lower_raw(name) == "cookie" end) do
1734
1737
{[], _headers} ->
1735
1738
headers
1736
1739
 
@@ -2215,8 +2218,8 @@ defmodule Mint.HTTP2 do
2215
2218
"can't send more data on this request since it's not streaming"
2216
2219
end
2217
2220
2218
- def format_error({:unallowed_trailing_header, {name, value}}) do
2219
- "header #{inspect(name)} (with value #{inspect(value)}) is not allowed as a trailer header"
2221
+ def format_error({:unallowed_trailing_header, name}) do
2222
+ "header #{inspect(name)} is not allowed as a trailer header"
2220
2223
end
2221
2224
2222
2225
def format_error(:missing_status_header) do
changed lib/mint/http2/frame.ex
 
@@ -66,7 +66,6 @@ defmodule Mint.HTTP2.Frame do
66
66
for {frame, flags} <- @flags,
67
67
{flag_name, flag_value} <- flags do
68
68
defp set_flag(flags, unquote(frame), unquote(flag_name)), do: bor(flags, unquote(flag_value))
69
- defp set_flag(unquote(frame), unquote(flag_name)), do: unquote(flag_value)
70
69
71
70
def flag_set?(flags, unquote(frame), unquote(flag_name)),
72
71
do: band(flags, unquote(flag_value)) == unquote(flag_value)
changed lib/mint/negotiate.ex
 
@@ -1,8 +1,6 @@
1
1
defmodule Mint.Negotiate do
2
2
@moduledoc false
3
3
4
- import Mint.Core.Util
5
-
6
4
alias Mint.{
7
5
HTTP1,
8
6
HTTP2,
 
@@ -10,6 +8,8 @@ defmodule Mint.Negotiate do
10
8
Types
11
9
}
12
10
11
+ alias Mint.Core.Util
12
+
13
13
@default_protocols [:http1, :http2]
14
14
@transport_opts [alpn_advertised_protocols: ["http/1.1", "h2"]]
15
15
 
@@ -68,7 +68,7 @@ defmodule Mint.Negotiate do
68
68
end
69
69
70
70
defp connect_negotiate(scheme, address, port, opts) do
71
- transport = scheme_to_transport(scheme)
71
+ transport = Util.scheme_to_transport(scheme)
72
72
hostname = Mint.Core.Util.hostname(opts, address)
73
73
74
74
transport_opts =
 
@@ -106,7 +106,7 @@ defmodule Mint.Negotiate do
106
106
end
107
107
108
108
defp connect_upgrade(proxy_scheme, transport_state, new_scheme, hostname, port, opts) do
109
- transport = scheme_to_transport(new_scheme)
109
+ transport = Util.scheme_to_transport(new_scheme)
110
110
111
111
transport_opts =
112
112
opts
 
@@ -123,7 +123,7 @@ defmodule Mint.Negotiate do
123
123
end
124
124
125
125
defp alpn_negotiate(scheme, socket, hostname, port, opts) do
126
- transport = scheme_to_transport(scheme)
126
+ transport = Util.scheme_to_transport(scheme)
127
127
128
128
case transport.negotiated_protocol(socket) do
129
129
{:ok, "http/1.1"} ->
changed lib/mint/unsafe_proxy.ex
 
@@ -58,8 +58,8 @@ defmodule Mint.UnsafeProxy do
58
58
end
59
59
60
60
@impl true
61
- @spec open?(t(), :read | :write | :read_write) :: boolean()
62
- def open?(%UnsafeProxy{module: module, state: state}, type \\ :read_write) do
61
+ @spec open?(t(), :read | :write) :: boolean()
62
+ def open?(%UnsafeProxy{module: module, state: state}, type \\ :write) do
63
63
module.open?(state, type)
64
64
end
changed mix.exs
 
@@ -1,14 +1,14 @@
1
1
defmodule Mint.MixProject do
2
2
use Mix.Project
3
3
4
- @version "1.5.2"
4
+ @version "1.6.0"
5
5
@repo_url "https://github.com/elixir-mint/mint"
6
6
7
7
def project do
8
8
[
9
9
app: :mint,
10
10
version: @version,
11
- elixir: "~> 1.10",
11
+ elixir: "~> 1.11",
12
12
start_permanent: Mix.env() == :prod,
13
13
elixirc_paths: elixirc_paths(Mix.env()),
14
14
deps: deps(),
 
@@ -72,14 +72,14 @@ defmodule Mint.MixProject do
72
72
defp deps do
73
73
[
74
74
{:castore, "~> 0.1.0 or ~> 1.0", optional: true},
75
- {:hpax, "~> 0.1.1"},
75
+ {:hpax, "~> 0.1.1 or ~> 0.2.0"},
76
76
77
77
# Dev/test dependencies
78
- {:dialyxir, "~> 1.3.0", only: [:dev, :test], runtime: false},
78
+ {:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false},
79
79
{:ex_doc, "~> 0.20", only: :dev},
80
- {:excoveralls, "~> 0.17.0", only: :test},
80
+ {:excoveralls, "~> 0.18.0", only: :test},
81
81
{:mox, "~> 1.0", only: :test},
82
- {:stream_data, "~> 0.5.0", only: [:dev, :test]}
82
+ {:stream_data, "~> 0.6.0", only: [:dev, :test]}
83
83
]
84
84
end
85
85
end