aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/src/guide/listeners.asciidoc25
-rw-r--r--doc/src/manual/ranch.set_transport_options.asciidoc26
-rw-r--r--src/ranch.erl18
-rw-r--r--src/ranch_acceptors_sup.erl10
-rw-r--r--src/ranch_conns_sup.erl35
-rw-r--r--src/ranch_conns_sup_sup.erl15
-rw-r--r--src/ranch_listener_sup.erl13
-rw-r--r--src/ranch_server.erl2
-rw-r--r--test/acceptor_SUITE.erl31
9 files changed, 118 insertions, 57 deletions
diff --git a/doc/src/guide/listeners.asciidoc b/doc/src/guide/listeners.asciidoc
index 94552ba..df94896 100644
--- a/doc/src/guide/listeners.asciidoc
+++ b/doc/src/guide/listeners.asciidoc
@@ -358,14 +358,23 @@ Opts = ranch:get_protocol_options(tcp_echo).
=== Changing transport options
-Ranch allows you to change the transport options of a listener, for
-example to make it listen on a different port.
-
-To change transport options, the listener has to be suspended first.
-Then you are allowed to change the transport options by calling
-`ranch:set_transport_options/2` with the listener name and the new
-transport options as arguments.
-After that, you can resume the listener.
+Ranch allows you to change the transport options of a listener with
+the `ranch:set_transport_options/2` function, for example to change the
+number of acceptors or to make it listen on a different port.
+
+Changes to the following options will take effect...
+
+* immediately:
+** `max_connections`
+** `handshake_timeout`
+** `shutdown`
+* only after the listener has been suspended and resumed:
+** `num_acceptors`
+** `num_listen_sockets`
+** `socket_opts`
+* only when the entire listener is restarted:
+** `num_conns_sups`
+** `logger`
.Changing the transport options
diff --git a/doc/src/manual/ranch.set_transport_options.asciidoc b/doc/src/manual/ranch.set_transport_options.asciidoc
index cba6ba7..6c349ca 100644
--- a/doc/src/manual/ranch.set_transport_options.asciidoc
+++ b/doc/src/manual/ranch.set_transport_options.asciidoc
@@ -9,16 +9,25 @@ ranch:set_transport_options - Set the transport options
[source,erlang]
----
set_transport_options(Ref :: ranch:ref(),
- TransOpts :: any())
- -> ok | {error, running}
+ TransOpts :: ranch:opts())
+ -> ok | {error, Reason :: term()}
----
Set the transport options.
-The listener must be suspended for this call to succeed.
-If the listener is running, `{error, running}` will be returned.
+Changes to the following options will take effect...
-The change will take effect when the listener resumes.
+* immediately:
+** `max_connections`
+** `handshake_timeout`
+** `shutdown`
+* only after the listener has been suspended and resumed:
+** `num_acceptors`
+** `num_listen_sockets`
+** `socket_opts`
+* only when the entire listener is restarted:
+** `num_conns_sups`
+** `logger`
== Arguments
@@ -32,10 +41,15 @@ The new transport options.
== Return value
-The atom `ok` is always returned. It can be safely ignored.
+The atom `ok` is returned on success.
+
+An error tuple is returned on failure, for example if the given
+transport options contain invalid values.
== Changelog
+* *2.0*: The restriction that the listener must be suspended
+ has been removed.
* *2.0*: The `TransOpts` argument must no longer contain
Ranch-specific options if given as a list. Use a map.
diff --git a/src/ranch.erl b/src/ranch.erl
index 0e3541f..94727ea 100644
--- a/src/ranch.erl
+++ b/src/ranch.erl
@@ -291,16 +291,22 @@ set_max_connections(Ref, MaxConnections) ->
get_transport_options(Ref) ->
ranch_server:get_transport_options(Ref).
--spec set_transport_options(ref(), opts()) -> ok | {error, running}.
+-spec set_transport_options(ref(), opts()) -> ok | {error, term()}.
set_transport_options(Ref, TransOpts0) ->
TransOpts = normalize_opts(TransOpts0),
- case get_status(Ref) of
- suspended ->
- ok = ranch_server:set_transport_options(Ref, TransOpts);
- running ->
- {error, running}
+ case validate_transport_opts(TransOpts) of
+ ok ->
+ ok = ranch_server:set_transport_options(Ref, TransOpts),
+ ok = apply_transport_options(Ref, TransOpts);
+ TransOptsError ->
+ TransOptsError
end.
+apply_transport_options(Ref, TransOpts) ->
+ _ = [ConnsSup ! {set_transport_options, TransOpts}
+ || {_, ConnsSup} <- ranch_server:get_connections_sups(Ref)],
+ ok.
+
-spec get_protocol_options(ref()) -> any().
get_protocol_options(Ref) ->
ranch_server:get_protocol_options(Ref).
diff --git a/src/ranch_acceptors_sup.erl b/src/ranch_acceptors_sup.erl
index e08c2ec..b6531f8 100644
--- a/src/ranch_acceptors_sup.erl
+++ b/src/ranch_acceptors_sup.erl
@@ -18,15 +18,15 @@
-export([start_link/3]).
-export([init/1]).
--spec start_link(ranch:ref(), pos_integer(), module())
+-spec start_link(ranch:ref(), module(), module())
-> {ok, pid()}.
-start_link(Ref, NumAcceptors, Transport) ->
- supervisor:start_link(?MODULE, [Ref, NumAcceptors, Transport]).
+start_link(Ref, Transport, Logger) ->
+ supervisor:start_link(?MODULE, [Ref, Transport, Logger]).
-spec init([term()]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
-init([Ref, NumAcceptors, Transport]) ->
+init([Ref, Transport, Logger]) ->
TransOpts = ranch_server:get_transport_options(Ref),
- Logger = maps:get(logger, TransOpts, logger),
+ NumAcceptors = maps:get(num_acceptors, TransOpts, 10),
NumListenSockets = maps:get(num_listen_sockets, TransOpts, 1),
%% We temporarily put the logger in the process dictionary
%% so that it can be used from ranch:filter_options. The
diff --git a/src/ranch_conns_sup.erl b/src/ranch_conns_sup.erl
index a585e3f..389deb0 100644
--- a/src/ranch_conns_sup.erl
+++ b/src/ranch_conns_sup.erl
@@ -18,12 +18,12 @@
-module(ranch_conns_sup).
%% API.
--export([start_link/4]).
+-export([start_link/6]).
-export([start_protocol/3]).
-export([active_connections/1]).
%% Supervisor internals.
--export([init/5]).
+-export([init/7]).
-export([system_continue/3]).
-export([system_terminate/4]).
-export([system_code_change/4]).
@@ -46,10 +46,10 @@
%% API.
--spec start_link(ranch:ref(), pos_integer(), module(), module()) -> {ok, pid()}.
-start_link(Ref, Id, Transport, Protocol) ->
+-spec start_link(ranch:ref(), pos_integer(), module(), any(), module(), module()) -> {ok, pid()}.
+start_link(Ref, Id, Transport, TransOpts, Protocol, Logger) ->
proc_lib:start_link(?MODULE, init,
- [self(), Ref, Id, Transport, Protocol]).
+ [self(), Ref, Id, Transport, TransOpts, Protocol, Logger]).
%% We can safely assume we are on the same node as the supervisor.
%%
@@ -99,16 +99,14 @@ active_connections(SupPid) ->
%% Supervisor internals.
--spec init(pid(), ranch:ref(), pos_integer(), module(), module()) -> no_return().
-init(Parent, Ref, Id, Transport, Protocol) ->
+-spec init(pid(), ranch:ref(), pos_integer(), module(), any(), module(), module()) -> no_return().
+init(Parent, Ref, Id, Transport, TransOpts, Protocol, Logger) ->
process_flag(trap_exit, true),
ok = ranch_server:set_connections_sup(Ref, Id, self()),
MaxConns = ranch_server:get_max_connections(Ref),
- TransOpts = ranch_server:get_transport_options(Ref),
ConnType = maps:get(connection_type, TransOpts, worker),
Shutdown = maps:get(shutdown, TransOpts, 5000),
HandshakeTimeout = maps:get(handshake_timeout, TransOpts, 5000),
- Logger = maps:get(logger, TransOpts, logger),
ProtoOpts = ranch_server:get_protocol_options(Ref),
ok = proc_lib:init_ack(Parent, {ok, self()}),
loop(#state{parent=Parent, ref=Ref, conn_type=ConnType,
@@ -166,8 +164,11 @@ loop(State=#state{parent=Parent, ref=Ref, conn_type=ConnType,
{set_max_conns, MaxConns2} ->
loop(State#state{max_conns=MaxConns2},
CurConns, NbChildren, Sleepers);
+ %% Upgrade the transport options.
+ {set_transport_options, TransOpts} ->
+ set_transport_options(State, CurConns, NbChildren, Sleepers, TransOpts);
%% Upgrade the protocol options.
- {set_opts, Opts2} ->
+ {set_protocol_options, Opts2} ->
loop(State#state{opts=Opts2},
CurConns, NbChildren, Sleepers);
{'EXIT', Parent, Reason} ->
@@ -250,6 +251,20 @@ handshake(State=#state{ref=Ref, transport=Transport, handshake_timeout=Handshake
loop(State, CurConns, NbChildren, Sleepers)
end.
+set_transport_options(State=#state{max_conns=MaxConns0}, CurConns, NbChildren, Sleepers0, TransOpts) ->
+ MaxConns1 = maps:get(max_connections, TransOpts, 1024),
+ HandshakeTimeout = maps:get(handshake_timeout, TransOpts, 5000),
+ Shutdown = maps:get(shutdown, TransOpts, 5000),
+ Sleepers1 = case MaxConns1 > MaxConns0 of
+ true ->
+ _ = [To ! self() || To <- Sleepers0],
+ [];
+ false ->
+ Sleepers0
+ end,
+ loop(State#state{max_conns=MaxConns1, handshake_timeout=HandshakeTimeout, shutdown=Shutdown},
+ CurConns, NbChildren, Sleepers1).
+
-spec terminate(#state{}, any(), non_neg_integer()) -> no_return().
terminate(#state{shutdown=brutal_kill}, Reason, _) ->
kill_children(get_keys(active)),
diff --git a/src/ranch_conns_sup_sup.erl b/src/ranch_conns_sup_sup.erl
index 4b1dd47..61ace8e 100644
--- a/src/ranch_conns_sup_sup.erl
+++ b/src/ranch_conns_sup_sup.erl
@@ -19,19 +19,22 @@
-export([start_link/4]).
-export([init/1]).
--spec start_link(ranch:ref(), pos_integer(), ranch:opts(), module()) -> {ok, pid()}.
-start_link(Ref, NumConnsSups, Transport, Protocol) ->
+-spec start_link(ranch:ref(), module(), module(), module()) -> {ok, pid()}.
+start_link(Ref, Transport, Protocol, Logger) ->
ok = ranch_server:cleanup_connections_sups(Ref),
supervisor:start_link(?MODULE, {
- Ref, NumConnsSups, Transport, Protocol
+ Ref, Transport, Protocol, Logger
}).
--spec init({ranch:ref(), pos_integer(), module(), module()})
+-spec init({ranch:ref(), module(), module(), module()})
-> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
-init({Ref, NumConnsSups, Transport, Protocol}) ->
+init({Ref, Transport, Protocol, Logger}) ->
+ TransOpts = ranch_server:get_transport_options(Ref),
+ NumAcceptors = maps:get(num_acceptors, TransOpts, 10),
+ NumConnsSups = maps:get(num_conns_sups, TransOpts, NumAcceptors),
ChildSpecs = [#{
id => {ranch_conns_sup, N},
- start => {ranch_conns_sup, start_link, [Ref, N, Transport, Protocol]},
+ start => {ranch_conns_sup, start_link, [Ref, N, Transport, TransOpts, Protocol, Logger]},
type => supervisor
} || N <- lists:seq(1, NumConnsSups)],
{ok, {#{intensity => 1 + ceil(math:log2(NumConnsSups))}, ChildSpecs}}.
diff --git a/src/ranch_listener_sup.erl b/src/ranch_listener_sup.erl
index f4d8ebc..393099f 100644
--- a/src/ranch_listener_sup.erl
+++ b/src/ranch_listener_sup.erl
@@ -21,28 +21,27 @@
-spec start_link(ranch:ref(), module(), any(), module(), any())
-> {ok, pid()}.
start_link(Ref, Transport, TransOpts, Protocol, ProtoOpts) ->
- NumAcceptors = maps:get(num_acceptors, TransOpts, 10),
- NumConnsSups = maps:get(num_conns_sups, TransOpts, NumAcceptors),
MaxConns = maps:get(max_connections, TransOpts, 1024),
+ Logger = maps:get(logger, TransOpts, logger),
ranch_server:set_new_listener_opts(Ref, MaxConns, TransOpts, ProtoOpts,
[Ref, Transport, TransOpts, Protocol, ProtoOpts]),
supervisor:start_link(?MODULE, {
- Ref, NumAcceptors, NumConnsSups, Transport, Protocol
+ Ref, Transport, Protocol, Logger
}).
--spec init({ranch:ref(), pos_integer(), pos_integer(), module(), module()})
+-spec init({ranch:ref(), module(), module(), module()})
-> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
-init({Ref, NumAcceptors, NumConnsSups, Transport, Protocol}) ->
+init({Ref, Transport, Protocol, Logger}) ->
ok = ranch_server:set_listener_sup(Ref, self()),
ChildSpecs = [
#{
id => ranch_conns_sup_sup,
- start => {ranch_conns_sup_sup, start_link, [Ref, NumConnsSups, Transport, Protocol]},
+ start => {ranch_conns_sup_sup, start_link, [Ref, Transport, Protocol, Logger]},
type => supervisor
},
#{
id => ranch_acceptors_sup,
- start => {ranch_acceptors_sup, start_link, [Ref, NumAcceptors, Transport]},
+ start => {ranch_acceptors_sup, start_link, [Ref, Transport, Logger]},
type => supervisor
}
],
diff --git a/src/ranch_server.erl b/src/ranch_server.erl
index ae68e99..7bf8a96 100644
--- a/src/ranch_server.erl
+++ b/src/ranch_server.erl
@@ -202,7 +202,7 @@ handle_call({set_trans_opts, Ref, Opts}, _, State) ->
{reply, ok, State};
handle_call({set_proto_opts, Ref, Opts}, _, State) ->
ets:insert(?TAB, {{proto_opts, Ref}, Opts}),
- _ = [ConnsSup ! {set_opts, Opts} || {_, ConnsSup} <- get_connections_sups(Ref)],
+ _ = [ConnsSup ! {set_protocol_options, Opts} || {_, ConnsSup} <- get_connections_sups(Ref)],
{reply, ok, State};
handle_call(_Request, _From, State) ->
{reply, ignore, State}.
diff --git a/test/acceptor_SUITE.erl b/test/acceptor_SUITE.erl
index 332967f..1cccabc 100644
--- a/test/acceptor_SUITE.erl
+++ b/test/acceptor_SUITE.erl
@@ -73,6 +73,7 @@ groups() ->
misc_info,
misc_info_embedded,
misc_opts_logger,
+ misc_set_transport_options,
misc_wait_for_connections
]}, {supervisor, [
connection_type_supervisor,
@@ -323,6 +324,28 @@ misc_repeated_remove(_) ->
true = lists:all(fun ({_, ConnsSup}) -> erlang:is_process_alive(ConnsSup) end, ConnsSups),
ok = ranch:stop_listener(Name).
+misc_set_transport_options(_) ->
+ doc(""),
+ Name = name(),
+ {ok, ListenerSupPid} = ranch:start_listener(Name, ranch_tcp, #{max_connections => 10,
+ handshake_timeout => 5000, shutdown => 1000, num_acceptors => 1,
+ socket_opts => [{send_timeout, 5000}]}, echo_protocol, []),
+ ok = ranch:set_transport_options(Name, #{max_connections => 20, handshake_timeout => 5001,
+ num_acceptors => 2, shutdown => 1001, socket_opts => [{send_timeout, 5002}]}),
+ ConnsSups = [ConnsSup || {_, ConnsSup} <- ranch_server:get_connections_sups(Name)],
+ _ = [begin
+ {State, _, _, _} = sys:get_state(ConnsSup),
+ 20 = element(10, State),
+ 5001 = element(9, State),
+ 1001 = element(5, State)
+ end || ConnsSup <- ConnsSups],
+ ok = ranch:suspend_listener(Name),
+ ok = ranch:resume_listener(Name),
+ 2 = length(ranch:procs(Name, acceptors)),
+ LSocket = do_get_listener_socket(ListenerSupPid),
+ {ok, [{send_timeout, 5002}]} = ranch_tcp:getopts(LSocket, [send_timeout]),
+ ok = ranch:stop_listener(Name).
+
misc_wait_for_connections(_) ->
doc("Ensure wait for connections works."),
Name = name(),
@@ -621,8 +644,6 @@ ssl_graceful(_) ->
[binary, {active, false}, {packet, raw}]),
ok = ssl:send(Socket1, <<"SSL with fresh listener">>),
{ok, <<"SSL with fresh listener">>} = ssl:recv(Socket1, 23, 1000),
- %% Make sure transport options cannot be changed on a running listener.
- {error, running} = ranch:set_transport_options(Name, #{socket_opts => [{port, Port}|Opts]}),
%% Suspend listener, make sure established connections keep running.
ok = ranch:suspend_listener(Name),
suspended = ranch:get_status(Name),
@@ -640,8 +661,6 @@ ssl_graceful(_) ->
[binary, {active, false}, {packet, raw}]),
ok = ssl:send(Socket2, <<"SSL with resumed listener">>),
{ok, <<"SSL with resumed listener">>} = ssl:recv(Socket2, 25, 1000),
- %% Make sure transport options cannot be changed on resumed listener.
- {error, running} = ranch:set_transport_options(Name, #{socket_opts => [{port, Port}|Opts]}),
ok = ranch:stop_listener(Name),
{error, closed} = ssl:recv(Socket1, 0, 1000),
{error, closed} = ssl:recv(Socket2, 0, 1000),
@@ -860,8 +879,6 @@ tcp_graceful(_) ->
[binary, {active, false}, {packet, raw}]),
ok = gen_tcp:send(Socket1, <<"TCP with fresh listener">>),
{ok, <<"TCP with fresh listener">>} = gen_tcp:recv(Socket1, 23, 1000),
- %% Make sure transport options cannot be changed on a running listener.
- {error, running} = ranch:set_transport_options(Name, [{port, Port}]),
%% Suspend listener, make sure established connections keep running.
ok = ranch:suspend_listener(Name),
suspended = ranch:get_status(Name),
@@ -879,8 +896,6 @@ tcp_graceful(_) ->
[binary, {active, false}, {packet, raw}]),
ok = gen_tcp:send(Socket2, <<"TCP with resumed listener">>),
{ok, <<"TCP with resumed listener">>} = gen_tcp:recv(Socket2, 25, 1000),
- %% Make sure transport options cannot be changed on resumed listener.
- {error, running} = ranch:set_transport_options(Name, [{port, Port}]),
ok = ranch:stop_listener(Name),
{error, closed} = gen_tcp:recv(Socket1, 0, 1000),
{error, closed} = gen_tcp:recv(Socket2, 0, 1000),