aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2015-04-08 23:34:08 +0300
committerLoïc Hoguin <[email protected]>2015-04-08 23:34:08 +0300
commit8366ba94bb9e450221a246acdd482c0162affcd9 (patch)
tree5797de02c7f00760d50136c33502eaee654378dd
parent335eb50a06c574fb9dfb50cac6185aa18e8c3834 (diff)
downloadgun-8366ba94bb9e450221a246acdd482c0162affcd9.tar.gz
gun-8366ba94bb9e450221a246acdd482c0162affcd9.tar.bz2
gun-8366ba94bb9e450221a246acdd482c0162affcd9.zip
Use maps for and improve options
The type option has been removed. The transport and protocols options can be used in its place. The transport_opts option can be used to specify transport options. The http_opts and spdy_opts options can be used to specify protocol specific options. The keepalive option is now a protocol specific option. Defaults depending on the port number have changed. Now only port 443 uses ssl by default, other ports use tcp.
-rw-r--r--doc/src/guide/connect.asciidoc24
-rw-r--r--doc/src/manual/gun.asciidoc66
-rw-r--r--src/gun.erl213
-rw-r--r--src/gun_http.erl22
-rw-r--r--src/gun_spdy.erl11
-rw-r--r--test/ws_SUITE.erl2
6 files changed, 214 insertions, 124 deletions
diff --git a/doc/src/guide/connect.asciidoc b/doc/src/guide/connect.asciidoc
index e1ad56e..e2bcaa7 100644
--- a/doc/src/guide/connect.asciidoc
+++ b/doc/src/guide/connect.asciidoc
@@ -33,16 +33,24 @@ The `gun:open/{2,3}` function must be used to open a connection.
[source,erlang]
{ok, ConnPid} = gun:open("example.org", 443).
-@todo open/3
-@todo make opts a map
+If the port given is 443, Gun will attempt to connect using
+SSL. The protocol will be selected automatically using the
+NPN extension for TLS. By default Gun supports SPDY/3.1,
+SPDY/3 and HTTP/1.1 when connecting using SSL.
-If the port given is 80, Gun will attempt to connect using
-TCP and use the HTTP/1.1 protocol. For any other port, TLS
-will be used. The NPN extension for TLS allows Gun to select
-SPDY automatically if the server supports it. Otherwise,
-HTTP/1.1 will be used.
+For any other port, Gun will attempt to connect using TCP
+and will use the HTTP/1.1 protocol.
-@todo more about defaults
+The transport and protocol used can be overriden using
+options. The manual documents all available options.
+
+Options can be provided as a third argument, and take the
+form of a map.
+
+.Opening an SSL connection to example.org on port 8443
+
+[source,erlang]
+{ok, ConnPid} = gun:open("example.org", 8443, #{transport=>ssl}).
=== Monitoring the connection process
diff --git a/doc/src/manual/gun.asciidoc b/doc/src/manual/gun.asciidoc
index 14641ee..9d592a8 100644
--- a/doc/src/manual/gun.asciidoc
+++ b/doc/src/manual/gun.asciidoc
@@ -12,30 +12,62 @@ HTTP or Websocket.
== Types
-=== opts() = [opt()]
+=== opts() = map()
Configuration for the connection.
-@todo Should be a map.
-
-With opt():
-
-keepalive => pos_integer()::
- Time between pings in milliseconds.
- Defaults to 5000.
+The following keys are defined:
+
+http_opts => gun:http_opts()::
+ Options specific to the HTTP protocol. See below.
+protocols => [http | spdy]::
+ Ordered list of preferred protocols. When the transport is tcp,
+ this list must contain exactly one protocol. When the transport
+ is ssl, this list must contain at least one protocol and will be
+ used using the NPN protocol negotiation method. When the server
+ does not support NPN then http will always be used. Defaults to
+ [http] when the transport is tcp, and [spdy, http] when the
+ transport is ssl.
retry => non_neg_integer()::
Number of times Gun will try to reconnect on failure before giving up.
Defaults to 5.
retry_timeout => pos_integer()::
- Time between retries in milliseconds.
- Defaults to 5000.
-type => ssl | tcp | tcp_spdy::
- Whether to use SSL, plain TCP (for HTTP/Websocket) or SPDY over TCP.
- The default varies depending on the port used. Port 443 defaults
- to ssl. Port 6121 defaults to tcp_spdy (@todo). All other ports
- default to tcp. (@todo)
-
-@todo We want to separate protocol and transport options.
+ Time between retries in milliseconds. Defaults to 5000.
+spdy_opts => gun:spdy_opts()::
+ Options specific to the SPDY protocol. See below.
+trace => boolean()::
+ Whether to enable `dbg` tracing of the connection process. Should
+ only be used during debugging. Defaults to false.
+transport => tcp | ssl::
+ Whether to use SSL or plain TCP. The default varies depending on the
+ port used. Port 443 defaults to ssl. All other ports default to tcp.
+transport_opts => proplists:proplist()::
+ Transport options. They are TCP options or SSL options depending on
+ the selected transport.
+
+=== http_opts() = map()
+
+Configuration for the HTTP protocol.
+
+The following keys are defined:
+
+keepalive => pos_integer()::
+ Time between pings in milliseconds. Since the HTTP protocol has
+ no standardized way to ping the server, Gun will simply send an
+ empty line when the connection is idle. Gun only makes a best
+ effort here as servers usually have configurable limits to drop
+ idle connections. Defaults to 5000.
+version => 'HTTP/1.1' | 'HTTP/1.0'::
+ HTTP version to use. Defaults to 'HTTP/1.1'.
+
+=== spdy_opts() = map()
+
+Configuration for the SPDY protocol.
+
+The following keys are defined:
+
+keepalive => pos_integer()::
+ Time between pings in milliseconds. Defaults to 5000.
@todo We need to document Websocket options.
diff --git a/src/gun.erl b/src/gun.erl
index b7260bc..0fddfe3 100644
--- a/src/gun.erl
+++ b/src/gun.erl
@@ -71,7 +71,6 @@
-export([system_terminate/4]).
-export([system_code_change/4]).
--type conn_type() :: ssl | tcp | tcp_spdy.
-type headers() :: [{binary(), iodata()}].
-type ws_close_code() :: 1000..4999.
@@ -79,13 +78,15 @@
| {text | binary | close | ping | pong, iodata()}
| {close, ws_close_code(), iodata()}.
--type opts() :: [{http, gun_http:opts()}
- | {keepalive, pos_integer()}
- | {retry, non_neg_integer()}
- | {retry_timeout, pos_integer()}
- | {type, conn_type()}].
+-type opts() :: map().
-export_type([opts/0]).
+-type http_opts() :: map().
+-export_type([http_opts/0]).
+
+-type spdy_opts() :: map().
+-export_type([spdy_opts/0]).
+
-type ws_opts() :: [{compress, boolean()}].
-record(state, {
@@ -93,15 +94,11 @@
owner :: pid(),
host :: inet:hostname(),
port :: inet:port_number(),
- keepalive :: pos_integer(),
+ opts :: opts(),
keepalive_ref :: reference(),
- type :: conn_type(),
- retry :: non_neg_integer(),
- retry_timeout :: pos_integer(),
socket :: inet:socket() | ssl:sslsocket(),
transport :: module(),
protocol :: module(),
- proto_opts :: gun_http:opts(), %% @todo Make a tuple with SPDY too.
protocol_state :: any()
}).
@@ -110,35 +107,78 @@
-spec open(inet:hostname(), inet:port_number())
-> {ok, pid()} | {error, any()}.
open(Host, Port) ->
- open(Host, Port, []).
+ open(Host, Port, #{}).
-spec open(inet:hostname(), inet:port_number(), opts())
-> {ok, pid()} | {error, any()}.
open(Host, Port, Opts) when is_list(Host); is_atom(Host) ->
- case open_opts(Opts) of
+ case check_options(maps:to_list(Opts)) of
ok ->
- supervisor:start_child(gun_sup, [self(), Host, Port, Opts]);
- Error ->
- Error
+ case supervisor:start_child(gun_sup, [self(), Host, Port, Opts]) of
+ OK = {ok, ServerPid} ->
+ consider_tracing(ServerPid, Opts),
+ OK;
+ StartError ->
+ StartError
+ end;
+ CheckError ->
+ CheckError
end.
-%% @private
-open_opts([]) ->
+check_options([]) ->
ok;
-open_opts([{http, O}|Opts]) when is_list(O) ->
- open_opts(Opts);
-open_opts([{keepalive, K}|Opts]) when is_integer(K), K > 0 ->
- open_opts(Opts);
-open_opts([{retry, R}|Opts]) when is_integer(R), R >= 0 ->
- open_opts(Opts);
-open_opts([{retry_timeout, T}|Opts]) when is_integer(T) > 0 ->
- open_opts(Opts);
-open_opts([{type, T}|Opts])
- when T =:= tcp; T =:= tcp_spdy; T =:= ssl ->
- open_opts(Opts);
-open_opts([Opt|_]) ->
+check_options([{http_opts, ProtoOpts}|Opts]) when is_map(ProtoOpts) ->
+ case gun_http:check_options(map:to_list(ProtoOpts)) of
+ ok ->
+ check_options(Opts);
+ Error ->
+ Error
+ end;
+check_options([Opt = {protocols, L}|Opts]) when is_list(L) ->
+ Len = length(L),
+ case length(lists:usort(L)) of
+ Len when Len > 0 ->
+ Check = lists:usort([(P =:= http) orelse (P =:= spdy) || P <- L]),
+ case Check of
+ [true] ->
+ check_options(Opts);
+ _ ->
+ {error, {options, Opt}}
+ end;
+ _ ->
+ {error, {options, Opt}}
+ end;
+check_options([{retry, R}|Opts]) when is_integer(R), R >= 0 ->
+ check_options(Opts);
+check_options([{retry_timeout, T}|Opts]) when is_integer(T) > 0 ->
+ check_options(Opts);
+check_options([{spdy_opts, ProtoOpts}|Opts]) when is_map(ProtoOpts) ->
+ case gun_spdy:check_options(map:to_list(ProtoOpts)) of
+ ok ->
+ check_options(Opts);
+ Error ->
+ Error
+ end;
+check_options([{trace, B}|Opts]) when B =:= true; B =:= false ->
+ check_options(Opts);
+check_options([{transport, T}|Opts]) when T =:= tcp; T =:= ssl ->
+ check_options(Opts);
+check_options([{transport_opts, L}|Opts]) when is_list(L) ->
+ check_options(Opts);
+check_options([Opt|_]) ->
{error, {options, Opt}}.
+consider_tracing(ServerPid, #{trace := true}) ->
+ dbg:start(),
+ dbg:tracer(),
+ dbg:tpl(gun, [{'_', [], [{return_trace}]}]),
+ dbg:tpl(gun_http, [{'_', [], [{return_trace}]}]),
+ dbg:tpl(gun_spdy, [{'_', [], [{return_trace}]}]),
+ dbg:tpl(gun_ws, [{'_', [], [{return_trace}]}]),
+ dbg:p(ServerPid, all);
+consider_tracing(_, _) ->
+ ok.
+
-spec close(pid()) -> ok.
close(ServerPid) ->
supervisor:terminate_child(gun_sup, ServerPid).
@@ -372,62 +412,49 @@ start_link(Owner, Host, Port, Opts) ->
proc_lib:start_link(?MODULE, init,
[self(), Owner, Host, Port, Opts]).
-%% @doc Faster alternative to proplists:get_value/3.
-%% @private
-get_value(Key, Opts, Default) ->
- case lists:keyfind(Key, 1, Opts) of
- {_, Value} -> Value;
- _ -> Default
- end.
-
init(Parent, Owner, Host, Port, Opts) ->
ok = proc_lib:init_ack(Parent, {ok, self()}),
- HTTPOpts = get_value(http, Opts, []),
- Keepalive = get_value(keepalive, Opts, 5000),
- Retry = get_value(retry, Opts, 5),
- RetryTimeout = get_value(retry_timeout, Opts, 5000),
- %% Default to TCP if port 80 is given, otherwise SSL.
- Type = get_value(type, Opts, if Port =:= 80 -> tcp; true -> ssl end),
- connect(#state{parent=Parent, owner=Owner, host=Host, port=Port,
- keepalive=Keepalive, type=Type, retry=Retry,
- proto_opts=HTTPOpts, retry_timeout=RetryTimeout}, Retry).
-
-connect(State=#state{owner=Owner, host=Host, port=Port, type=ssl,
- proto_opts=HTTPOpts}, Retries) ->
- Transport = ranch_ssl,
- %% R15 support.
- HasNPN = erlang:function_exported(ssl, negotiated_next_protocol, 1),
- Opts = [binary, {active, false}
- |[{client_preferred_next_protocols,
- {client, [<<"spdy/3">>, <<"http/1.1">>], <<"http/1.1">>}}
- || HasNPN]],
- case Transport:connect(Host, Port, Opts) of
+ Retry = maps:get(retry, Opts, 5),
+ Transport = case maps:get(transport, Opts, default_transport(Port)) of
+ tcp -> ranch_tcp;
+ ssl -> ranch_ssl
+ end,
+ connect(#state{parent=Parent, owner=Owner, host=Host, port=Port, opts=Opts, transport=Transport}, Retry).
+
+default_transport(443) -> ssl;
+default_transport(_) -> tcp.
+
+connect(State=#state{owner=Owner, host=Host, port=Port, opts=Opts, transport=Transport=ranch_ssl}, Retries) ->
+ Protocols = lists:flatten([case P of
+ http -> <<"http/1.1">>;
+ spdy -> [<<"spdy/3.1">>, <<"spdy/3">>]
+ end || P <- maps:get(protocols, Opts, [spdy, http])]),
+ TransportOpts = [binary, {active, false},
+ {client_preferred_next_protocols, {client, Protocols, <<"http/1.1">>}}
+ |maps:get(transport_opts, Opts, [])],
+ case Transport:connect(Host, Port, TransportOpts) of
{ok, Socket} ->
- {Protocol, ProtoOpts} = case HasNPN of
- false ->
- {gun_http, HTTPOpts};
- true ->
- case ssl:negotiated_next_protocol(Socket) of
- {ok, <<"spdy/3">>} -> {gun_spdy, []};
- _ -> {gun_http, HTTPOpts}
- end
+ {Protocol, ProtoOptsKey} = case ssl:negotiated_next_protocol(Socket) of
+ {ok, <<"spdy/3", _/bits>>} -> {gun_spdy, spdy_opts};
+ _ -> {gun_http, http_opts}
end,
+ ProtoOpts = maps:get(ProtoOptsKey, Opts, #{}),
ProtoState = Protocol:init(Owner, Socket, Transport, ProtoOpts),
before_loop(State#state{socket=Socket, transport=Transport,
protocol=Protocol, protocol_state=ProtoState});
{error, _} ->
retry(State, Retries - 1)
end;
-connect(State=#state{owner=Owner, host=Host, port=Port, type=Type,
- proto_opts=HTTPOpts}, Retries) ->
- Transport = ranch_tcp,
- Opts = [binary, {active, false}],
- case Transport:connect(Host, Port, Opts) of
+connect(State=#state{owner=Owner, host=Host, port=Port, opts=Opts, transport=Transport}, Retries) ->
+ TransportOpts = [binary, {active, false}
+ |maps:get(transport_opts, Opts, [])],
+ case Transport:connect(Host, Port, TransportOpts) of
{ok, Socket} ->
- {Protocol, ProtoOpts} = case Type of
- tcp_spdy -> {gun_spdy, []};
- tcp -> {gun_http, HTTPOpts}
+ {Protocol, ProtoOptsKey} = case maps:get(protocols, Opts, [http]) of
+ [http] -> {gun_http, http_opts};
+ [spdy] -> {gun_spdy, spdy_opts}
end,
+ ProtoOpts = maps:get(ProtoOptsKey, Opts, #{}),
ProtoState = Protocol:init(Owner, Socket, Transport, ProtoOpts),
before_loop(State#state{socket=Socket, transport=Transport,
protocol=Protocol, protocol_state=ProtoState});
@@ -435,6 +462,9 @@ connect(State=#state{owner=Owner, host=Host, port=Port, type=Type,
retry(State, Retries - 1)
end.
+retry(State=#state{opts=Opts}) ->
+ retry(State, maps:get(retry, Opts, 5)).
+
%% Exit normally if the retry functionality has been disabled.
retry(_, 0) ->
ok;
@@ -453,8 +483,8 @@ retry(State, Retries) ->
%% Too many retries, give up.
retry_loop(_, 0) ->
error(gone);
-retry_loop(State=#state{parent=Parent, retry_timeout=RetryTimeout}, Retries) ->
- _ = erlang:send_after(RetryTimeout, self(), retry),
+retry_loop(State=#state{parent=Parent, opts=Opts}, Retries) ->
+ _ = erlang:send_after(maps:get(retry_timeout, Opts, 5000), self(), retry),
receive
retry ->
connect(State, Retries);
@@ -463,13 +493,18 @@ retry_loop(State=#state{parent=Parent, retry_timeout=RetryTimeout}, Retries) ->
{retry_loop, State, Retries})
end.
-before_loop(State=#state{keepalive=Keepalive}) ->
+before_loop(State=#state{opts=Opts, protocol=Protocol}) ->
+ ProtoOptsKey = case Protocol of
+ gun_http -> http_opts;
+ gun_spdy -> spdy_opts
+ end,
+ ProtoOpts = maps:get(ProtoOptsKey, Opts, #{}),
+ Keepalive = maps:get(keepalive, ProtoOpts, 5000),
KeepaliveRef = erlang:send_after(Keepalive, self(), keepalive),
loop(State#state{keepalive_ref=KeepaliveRef}).
loop(State=#state{parent=Parent, owner=Owner, host=Host, port=Port,
- retry=Retry, socket=Socket, transport=Transport,
- protocol=Protocol, protocol_state=ProtoState}) ->
+ socket=Socket, transport=Transport, protocol=Protocol, protocol_state=ProtoState}) ->
{OK, Closed, Error} = Transport:messages(),
Transport:setopts(Socket, [{active, once}]),
receive
@@ -478,7 +513,7 @@ loop(State=#state{parent=Parent, owner=Owner, host=Host, port=Port,
close ->
Transport:close(Socket),
retry(State#state{socket=undefined, transport=undefined,
- protocol=undefined}, Retry);
+ protocol=undefined});
{upgrade, Protocol2, ProtoState2} ->
ws_loop(State#state{protocol=Protocol2, protocol_state=ProtoState2});
ProtoState2 ->
@@ -488,12 +523,12 @@ loop(State=#state{parent=Parent, owner=Owner, host=Host, port=Port,
Protocol:close(ProtoState),
Transport:close(Socket),
retry(State#state{socket=undefined, transport=undefined,
- protocol=undefined}, Retry);
+ protocol=undefined});
{Error, Socket, _} ->
Protocol:close(ProtoState),
Transport:close(Socket),
retry(State#state{socket=undefined, transport=undefined,
- protocol=undefined}, Retry);
+ protocol=undefined});
{OK, _PreviousSocket, _Data} ->
loop(State);
{Closed, _PreviousSocket} ->
@@ -518,8 +553,8 @@ loop(State=#state{parent=Parent, owner=Owner, host=Host, port=Port,
{cancel, Owner, StreamRef} ->
ProtoState2 = Protocol:cancel(ProtoState, StreamRef),
loop(State#state{protocol_state=ProtoState2});
- {ws_upgrade, Owner, StreamRef, Path, Headers, Opts} when Protocol =/= gun_spdy ->
- ProtoState2 = Protocol:ws_upgrade(ProtoState, StreamRef, Host, Port, Path, Headers, Opts),
+ {ws_upgrade, Owner, StreamRef, Path, Headers, WsOpts} when Protocol =/= gun_spdy ->
+ ProtoState2 = Protocol:ws_upgrade(ProtoState, StreamRef, Host, Port, Path, Headers, WsOpts),
loop(State#state{protocol_state=ProtoState2});
%% @todo can fail if http/1.0
{shutdown, Owner} ->
@@ -549,7 +584,7 @@ loop(State=#state{parent=Parent, owner=Owner, host=Host, port=Port,
loop(State)
end.
-ws_loop(State=#state{parent=Parent, owner=Owner, retry=Retry, socket=Socket,
+ws_loop(State=#state{parent=Parent, owner=Owner, socket=Socket,
transport=Transport, protocol=Protocol, protocol_state=ProtoState}) ->
{OK, Closed, Error} = Transport:messages(),
ok = Transport:setopts(Socket, [{active, once}]),
@@ -558,16 +593,16 @@ ws_loop(State=#state{parent=Parent, owner=Owner, retry=Retry, socket=Socket,
case Protocol:handle(Data, ProtoState) of
close ->
Transport:close(Socket),
- retry(State#state{socket=undefined, transport=undefined, protocol=undefined}, Retry);
+ retry(State#state{socket=undefined, transport=undefined, protocol=undefined});
ProtoState2 ->
ws_loop(State#state{protocol_state=ProtoState2})
end;
{Closed, Socket} ->
Transport:close(Socket),
- retry(State#state{socket=undefined, transport=undefined, protocol=undefined}, Retry);
+ retry(State#state{socket=undefined, transport=undefined, protocol=undefined});
{Error, Socket, _} ->
Transport:close(Socket),
- retry(State#state{socket=undefined, transport=undefined, protocol=undefined}, Retry);
+ retry(State#state{socket=undefined, transport=undefined, protocol=undefined});
%% Ignore any previous HTTP keep-alive.
keepalive ->
ws_loop(State);
@@ -577,7 +612,7 @@ ws_loop(State=#state{parent=Parent, owner=Owner, retry=Retry, socket=Socket,
case Protocol:send(Frame, ProtoState) of
close ->
Transport:close(Socket),
- retry(State#state{socket=undefined, transport=undefined, protocol=undefined}, Retry);
+ retry(State#state{socket=undefined, transport=undefined, protocol=undefined});
ProtoState2 ->
ws_loop(State#state{protocol_state=ProtoState2})
end;
diff --git a/src/gun_http.erl b/src/gun_http.erl
index 745c2a9..4726f03 100644
--- a/src/gun_http.erl
+++ b/src/gun_http.erl
@@ -14,6 +14,7 @@
-module(gun_http).
+-export([check_options/1]).
-export([init/4]).
-export([handle/2]).
-export([close/1]).
@@ -24,9 +25,6 @@
-export([cancel/2]).
-export([ws_upgrade/7]).
--type opts() :: [{version, cow_http:version()}].
--export_type([opts/0]).
-
-type io() :: head | {body, non_neg_integer()} | body_close | body_chunked.
-type websocket_info() :: {websocket, reference(), binary(), [], []}. %% key, extensions, protocols
@@ -44,11 +42,19 @@
out = head :: io()
}).
-init(Owner, Socket, Transport, []) ->
- #http_state{owner=Owner, socket=Socket, transport=Transport};
-init(Owner, Socket, Transport, [{version, Version}]) ->
- #http_state{owner=Owner, socket=Socket, transport=Transport,
- version=Version}.
+check_options(Opts) ->
+ do_check_options(map:to_list(Opts)).
+
+do_check_options([{keepalive, K}|Opts]) when is_integer(K), K > 0 ->
+ do_check_options(Opts);
+do_check_options([{version, V}|Opts]) when V =:= 'HTTP/1.1'; V =:= 'HTTP/1.0' ->
+ do_check_options(Opts);
+do_check_options([Opt|_]) ->
+ {error, {options, {http, Opt}}}.
+
+init(Owner, Socket, Transport, Opts) ->
+ Version = maps:get(version, Opts, 'HTTP/1.1'),
+ #http_state{owner=Owner, socket=Socket, transport=Transport, version=Version}.
%% Stop looping when we got no more data.
handle(<<>>, State) ->
diff --git a/src/gun_spdy.erl b/src/gun_spdy.erl
index 7651584..ee61659 100644
--- a/src/gun_spdy.erl
+++ b/src/gun_spdy.erl
@@ -14,6 +14,7 @@
-module(gun_spdy).
+-export([check_options/1]).
-export([init/4]).
-export([handle/2]).
-export([close/1]).
@@ -43,7 +44,15 @@
ping_id = 1 :: non_neg_integer()
}).
-init(Owner, Socket, Transport, []) ->
+check_options(Opts) ->
+ do_check_options(map:to_list(Opts)).
+
+do_check_options([{keepalive, K}|Opts]) when is_integer(K), K > 0 ->
+ do_check_options(Opts);
+do_check_options([Opt|_]) ->
+ {error, {options, {spdy, Opt}}}.
+
+init(Owner, Socket, Transport, _Opts) ->
#spdy_state{owner=Owner, socket=Socket, transport=Transport,
zdef=cow_spdy:deflate_init(), zinf=cow_spdy:inflate_init()}.
diff --git a/test/ws_SUITE.erl b/test/ws_SUITE.erl
index 82f3632..2310b31 100644
--- a/test/ws_SUITE.erl
+++ b/test/ws_SUITE.erl
@@ -140,7 +140,7 @@ log_output() ->
ok.
connect(Path) ->
- {ok, Pid} = gun:open("127.0.0.1", 33080, [{type, tcp}, {retry, 0}]),
+ {ok, Pid} = gun:open("127.0.0.1", 33080, #{retry=>0}),
Ref = monitor(process, Pid),
gun:ws_upgrade(Pid, Path, [], #{compress => true}),
receive