aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorj.uhlig <[email protected]>2018-03-13 09:31:20 +0100
committerLoïc Hoguin <[email protected]>2018-03-14 16:04:15 +0100
commitfa608621bfedbb981657d2d09e2b321dddc0ad8e (patch)
tree13efb8de52cb24c449e3defb26ac40da447d8923
parent132dd4dfee8a0cf6f2bd05d21538898bdbbc70ab (diff)
downloadranch-fa608621bfedbb981657d2d09e2b321dddc0ad8e.tar.gz
ranch-fa608621bfedbb981657d2d09e2b321dddc0ad8e.tar.bz2
ranch-fa608621bfedbb981657d2d09e2b321dddc0ad8e.zip
Fix ranch:info/0 and ranch:procs/2 in embedded mode
-rw-r--r--src/ranch.erl26
-rw-r--r--src/ranch_listener_sup.erl4
-rw-r--r--src/ranch_server.erl64
-rw-r--r--test/acceptor_SUITE.erl100
-rw-r--r--test/embedded_sup.erl28
5 files changed, 185 insertions, 37 deletions
diff --git a/src/ranch.erl b/src/ranch.erl
index 52c5679..d4adee5 100644
--- a/src/ranch.erl
+++ b/src/ranch.erl
@@ -164,12 +164,11 @@ set_protocol_options(Ref, Opts) ->
-spec info() -> [{any(), [{atom(), any()}]}].
info() ->
- Children = supervisor:which_children(ranch_sup),
[{Ref, listener_info(Ref, Pid)}
- || {{ranch_listener_sup, Ref}, Pid, _, [_]} <- Children].
+ || {Ref, Pid} <- ranch_server:get_listener_sups()].
listener_info(Ref, Pid) ->
- [_, NumAcceptors, Transport, TransOpts, Protocol, _] = listener_start_args(Ref),
+ [_, NumAcceptors, Transport, TransOpts, Protocol, _] = ranch_server:get_listener_start_args(Ref),
ConnsSup = ranch_server:get_connections_sup(Ref),
{IP, Port} = get_addr(Ref),
MaxConns = get_max_connections(Ref),
@@ -188,24 +187,6 @@ listener_info(Ref, Pid) ->
{protocol_options, ProtoOpts}
].
-listener_start_args(Ref) ->
- case erlang:function_exported(supervisor, get_childspec, 2) of
- true ->
- %% Can't use map syntax before R18.
- {ok, Map} = supervisor:get_childspec(ranch_sup, {ranch_listener_sup, Ref}),
- {ranch_listener_sup, start_link, StartArgs} = maps:get(start, Map),
- StartArgs;
- false ->
- %% Awful solution for compatibility with R16 and R17.
- {status, _, _, [_, _, _, _, [_, _,
- {data, [{_, {state, _, _, Children, _, _, _, _, _, _}}]}]]}
- = sys:get_status(ranch_sup),
- [StartArgs] = [StartArgs || {child, _, {ranch_listener_sup, ChildRef},
- {ranch_listener_sup, start_link, StartArgs}, _, _, _, _}
- <- Children, ChildRef =:= Ref],
- StartArgs
- end.
-
-spec procs(ref(), acceptors | connections) -> [pid()].
procs(Ref, acceptors) ->
procs1(Ref, ranch_acceptors_sup);
@@ -213,8 +194,7 @@ procs(Ref, connections) ->
procs1(Ref, ranch_conns_sup).
procs1(Ref, Sup) ->
- {_, ListenerSup, _, _} = lists:keyfind({ranch_listener_sup, Ref}, 1,
- supervisor:which_children(ranch_sup)),
+ ListenerSup = ranch_server:get_listener_sup(Ref),
{_, SupPid, _, _} = lists:keyfind(Sup, 1,
supervisor:which_children(ListenerSup)),
[Pid || {_, Pid, _, _} <- supervisor:which_children(SupPid)].
diff --git a/src/ranch_listener_sup.erl b/src/ranch_listener_sup.erl
index f2ca6a1..584dc36 100644
--- a/src/ranch_listener_sup.erl
+++ b/src/ranch_listener_sup.erl
@@ -22,12 +22,14 @@
-> {ok, pid()}.
start_link(Ref, NumAcceptors, Transport, TransOpts, Protocol, ProtoOpts) ->
MaxConns = proplists:get_value(max_connections, TransOpts, 1024),
- ranch_server:set_new_listener_opts(Ref, MaxConns, ProtoOpts),
+ ranch_server:set_new_listener_opts(Ref, MaxConns, ProtoOpts,
+ [Ref, NumAcceptors, Transport, TransOpts, Protocol, ProtoOpts]),
supervisor:start_link(?MODULE, {
Ref, NumAcceptors, Transport, TransOpts, Protocol
}).
init({Ref, NumAcceptors, Transport, TransOpts, Protocol}) ->
+ ok = ranch_server:set_listener_sup(Ref, self()),
AckTimeout = proplists:get_value(ack_timeout, TransOpts, 5000),
ConnType = proplists:get_value(connection_type, TransOpts, worker),
Shutdown = proplists:get_value(shutdown, TransOpts, 5000),
diff --git a/src/ranch_server.erl b/src/ranch_server.erl
index 8a17084..64f1f98 100644
--- a/src/ranch_server.erl
+++ b/src/ranch_server.erl
@@ -17,16 +17,21 @@
%% API.
-export([start_link/0]).
--export([set_new_listener_opts/3]).
+-export([set_new_listener_opts/4]).
-export([cleanup_listener_opts/1]).
-export([set_connections_sup/2]).
-export([get_connections_sup/1]).
+-export([get_connections_sups/0]).
+-export([set_listener_sup/2]).
+-export([get_listener_sup/1]).
+-export([get_listener_sups/0]).
-export([set_addr/2]).
-export([get_addr/1]).
-export([set_max_connections/2]).
-export([get_max_connections/1]).
-export([set_protocol_options/2]).
-export([get_protocol_options/1]).
+-export([get_listener_start_args/1]).
-export([count_connections/1]).
%% gen_server.
@@ -50,15 +55,16 @@
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
--spec set_new_listener_opts(ranch:ref(), ranch:max_conns(), any()) -> ok.
-set_new_listener_opts(Ref, MaxConns, Opts) ->
- gen_server:call(?MODULE, {set_new_listener_opts, Ref, MaxConns, Opts}).
+-spec set_new_listener_opts(ranch:ref(), ranch:max_conns(), any(), [any()]) -> ok.
+set_new_listener_opts(Ref, MaxConns, ProtoOpts, StartArgs) ->
+ gen_server:call(?MODULE, {set_new_listener_opts, Ref, MaxConns, ProtoOpts, StartArgs}).
-spec cleanup_listener_opts(ranch:ref()) -> ok.
cleanup_listener_opts(Ref) ->
_ = ets:delete(?TAB, {addr, Ref}),
_ = ets:delete(?TAB, {max_conns, Ref}),
_ = ets:delete(?TAB, {opts, Ref}),
+ _ = ets:delete(?TAB, {listener_start_args, Ref}),
%% We also remove the pid of the connections supervisor.
%% Depending on the timing, it might already have been deleted
%% when we handled the monitor DOWN message. However, in some
@@ -67,6 +73,8 @@ cleanup_listener_opts(Ref) ->
%% expected a crash (because the listener was stopped).
%% Deleting it explictly here removes any possible confusion.
_ = ets:delete(?TAB, {conns_sup, Ref}),
+ %% Ditto for the listener supervisor.
+ _ = ets:delete(?TAB, {listener_sup, Ref}),
ok.
-spec set_connections_sup(ranch:ref(), pid()) -> ok.
@@ -78,6 +86,23 @@ set_connections_sup(Ref, Pid) ->
get_connections_sup(Ref) ->
ets:lookup_element(?TAB, {conns_sup, Ref}, 2).
+-spec get_connections_sups() -> [{ranch:ref(), pid()}].
+get_connections_sups() ->
+ [{Ref, Pid} || [Ref, Pid] <- ets:match(?TAB, {{conns_sup, '$1'}, '$2'})].
+
+-spec set_listener_sup(ranch:ref(), pid()) -> ok.
+set_listener_sup(Ref, Pid) ->
+ true = gen_server:call(?MODULE, {set_listener_sup, Ref, Pid}),
+ ok.
+
+-spec get_listener_sup(ranch:ref()) -> pid().
+get_listener_sup(Ref) ->
+ ets:lookup_element(?TAB, {listener_sup, Ref}, 2).
+
+-spec get_listener_sups() -> [{ranch:ref(), pid()}].
+get_listener_sups() ->
+ [{Ref, Pid} || [Ref, Pid] <- ets:match(?TAB, {{listener_sup, '$1'}, '$2'})].
+
-spec set_addr(ranch:ref(), {inet:ip_address(), inet:port_number()}) -> ok.
set_addr(Ref, Addr) ->
gen_server:call(?MODULE, {set_addr, Ref, Addr}).
@@ -102,6 +127,10 @@ set_protocol_options(Ref, ProtoOpts) ->
get_protocol_options(Ref) ->
ets:lookup_element(?TAB, {opts, Ref}, 2).
+-spec get_listener_start_args(ranch:ref()) -> [any()].
+get_listener_start_args(Ref) ->
+ ets:lookup_element(?TAB, {listener_start_args, Ref}, 2).
+
-spec count_connections(ranch:ref()) -> non_neg_integer().
count_connections(Ref) ->
ranch_conns_sup:active_connections(get_connections_sup(Ref)).
@@ -109,13 +138,16 @@ count_connections(Ref) ->
%% gen_server.
init([]) ->
- Monitors = [{{erlang:monitor(process, Pid), Pid}, Ref} ||
+ ConnMonitors = [{{erlang:monitor(process, Pid), Pid}, {conns_sup, Ref}} ||
[Ref, Pid] <- ets:match(?TAB, {{conns_sup, '$1'}, '$2'})],
- {ok, #state{monitors=Monitors}}.
+ ListenerMonitors = [{{erlang:monitor(process, Pid), Pid}, {listener_sup, Ref}} ||
+ [Ref, Pid] <- ets:match(?TAB, {{listener_sup, '$1'}, '$2'})],
+ {ok, #state{monitors=ConnMonitors++ListenerMonitors}}.
-handle_call({set_new_listener_opts, Ref, MaxConns, Opts}, _, State) ->
+handle_call({set_new_listener_opts, Ref, MaxConns, ProtoOpts, StartArgs}, _, State) ->
ets:insert(?TAB, {{max_conns, Ref}, MaxConns}),
- ets:insert(?TAB, {{opts, Ref}, Opts}),
+ ets:insert(?TAB, {{opts, Ref}, ProtoOpts}),
+ ets:insert(?TAB, {{listener_start_args, Ref}, StartArgs}),
{reply, ok, State};
handle_call({set_connections_sup, Ref, Pid}, _,
State=#state{monitors=Monitors}) ->
@@ -123,7 +155,17 @@ handle_call({set_connections_sup, Ref, Pid}, _,
true ->
MonitorRef = erlang:monitor(process, Pid),
{reply, true,
- State#state{monitors=[{{MonitorRef, Pid}, Ref}|Monitors]}};
+ State#state{monitors=[{{MonitorRef, Pid}, {conns_sup, Ref}}|Monitors]}};
+ false ->
+ {reply, false, State}
+ end;
+handle_call({set_listener_sup, Ref, Pid}, _,
+ State=#state{monitors=Monitors}) ->
+ case ets:insert_new(?TAB, {{listener_sup, Ref}, Pid}) of
+ true ->
+ MonitorRef = erlang:monitor(process, Pid),
+ {reply, true,
+ State#state{monitors=[{{MonitorRef, Pid}, {listener_sup, Ref}}|Monitors]}};
false ->
{reply, false, State}
end;
@@ -148,8 +190,8 @@ handle_cast(_Request, State) ->
handle_info({'DOWN', MonitorRef, process, Pid, _},
State=#state{monitors=Monitors}) ->
- {_, Ref} = lists:keyfind({MonitorRef, Pid}, 1, Monitors),
- _ = ets:delete(?TAB, {conns_sup, Ref}),
+ {_, TypeRef} = lists:keyfind({MonitorRef, Pid}, 1, Monitors),
+ _ = ets:delete(?TAB, TypeRef),
Monitors2 = lists:keydelete({MonitorRef, Pid}, 1, Monitors),
{noreply, State#state{monitors=Monitors2}};
handle_info(_Info, State) ->
diff --git a/test/acceptor_SUITE.erl b/test/acceptor_SUITE.erl
index 645831b..285f438 100644
--- a/test/acceptor_SUITE.erl
+++ b/test/acceptor_SUITE.erl
@@ -55,7 +55,8 @@ groups() ->
]}, {misc, [
misc_bad_transport,
misc_bad_transport_options,
- misc_info
+ misc_info,
+ misc_info_embedded
]}, {supervisor, [
connection_type_supervisor,
connection_type_supervisor_separate_from_connection,
@@ -148,7 +149,7 @@ misc_info(_) ->
{protocol, remove_conn_and_wait_protocol},
{protocol_options, [{remove, false, 2500}]} %% Option was modified.
]}
- ] = lists:sort([L || L={{misc_info, _}, _} <- ranch:info()]),
+ ] = do_get_listener_info(misc_info),
%% Get acceptors.
[_] = ranch:procs({misc_info, tcp}, acceptors),
[_, _] = ranch:procs({misc_info, act}, acceptors),
@@ -159,6 +160,101 @@ misc_info(_) ->
[] = ranch:procs({misc_info, ssl}, connections),
ok.
+misc_info_embedded(_) ->
+ doc("Information about listeners in embedded mode."),
+ {ok, SupPid} = embedded_sup:start_link(),
+ %% Open a listener with a few connections.
+ {ok, Pid1} = embedded_sup:start_listener(SupPid, {misc_info_embedded, tcp}, ranch_tcp, [{num_acceptors, 1}], remove_conn_and_wait_protocol, [{remove, true, 2500}]),
+ Port1 = ranch:get_port({misc_info_embedded, tcp}),
+ %% Open a few more listeners with different arguments.
+ {ok, Pid2} = embedded_sup:start_listener(SupPid, {misc_info_embedded, act}, ranch_tcp, [{num_acceptors, 2}], active_echo_protocol, {}),
+ Port2 = ranch:get_port({misc_info_embedded, act}),
+ ranch:set_max_connections({misc_info_embedded, act}, infinity),
+ Opts = ct_helper:get_certs_from_ets(),
+ {ok, Pid3} = embedded_sup:start_listener(SupPid, {misc_info_embedded, ssl},
+ ranch_ssl, [{num_acceptors, 3}|Opts], echo_protocol, [{}]),
+ Port3 = ranch:get_port({misc_info_embedded, ssl}),
+ %% Open 5 connections, 3 removed from the count.
+ {ok, _} = gen_tcp:connect("localhost", Port1, [binary, {active, false}, {packet, raw}]),
+ {ok, _} = gen_tcp:connect("localhost", Port1, [binary, {active, false}, {packet, raw}]),
+ {ok, _} = gen_tcp:connect("localhost", Port1, [binary, {active, false}, {packet, raw}]),
+ receive after 250 -> ok end,
+ ranch:set_protocol_options({misc_info_embedded, tcp}, [{remove, false, 2500}]),
+ receive after 250 -> ok end,
+ {ok, _} = gen_tcp:connect("localhost", Port1, [binary, {active, false}, {packet, raw}]),
+ {ok, _} = gen_tcp:connect("localhost", Port1, [binary, {active, false}, {packet, raw}]),
+ receive after 250 -> ok end,
+ %% Confirm the info returned by Ranch is correct.
+ [
+ {{misc_info_embedded, act}, [
+ {pid, Pid2},
+ {ip, _},
+ {port, Port2},
+ {num_acceptors, 2},
+ {max_connections, infinity}, %% Option was modified.
+ {active_connections, 0},
+ {all_connections, 0},
+ {transport, ranch_tcp},
+ {transport_options, [{num_acceptors, 2}]},
+ {protocol, active_echo_protocol},
+ {protocol_options, {}}
+ ]},
+ {{misc_info_embedded, ssl}, [
+ {pid, Pid3},
+ {ip, _},
+ {port, Port3},
+ {num_acceptors, 3},
+ {max_connections, 1024},
+ {active_connections, 0},
+ {all_connections, 0},
+ {transport, ranch_ssl},
+ {transport_options, [{num_acceptors, 3}|Opts]},
+ {protocol, echo_protocol},
+ {protocol_options, [{}]}
+ ]},
+ {{misc_info_embedded, tcp}, [
+ {pid, Pid1},
+ {ip, _},
+ {port, Port1},
+ {num_acceptors, 1},
+ {max_connections, 1024},
+ {active_connections, 2},
+ {all_connections, 5},
+ {transport, ranch_tcp},
+ {transport_options, [{num_acceptors, 1}]},
+ {protocol, remove_conn_and_wait_protocol},
+ {protocol_options, [{remove, false, 2500}]} %% Option was modified.
+ ]}
+ ] = do_get_listener_info(misc_info_embedded),
+ %% Get acceptors.
+ [_] = ranch:procs({misc_info_embedded, tcp}, acceptors),
+ [_, _] = ranch:procs({misc_info_embedded, act}, acceptors),
+ [_, _, _] = ranch:procs({misc_info_embedded, ssl}, acceptors),
+ %% Get connections.
+ [_, _, _, _, _] = ranch:procs({misc_info_embedded, tcp}, connections),
+ [] = ranch:procs({misc_info_embedded, act}, connections),
+ [] = ranch:procs({misc_info_embedded, ssl}, connections),
+ %% Stop embedded tcp listener and ensure it is gone.
+ ok = embedded_sup:stop_listener(SupPid, {misc_info_embedded, tcp}),
+ timer:sleep(500),
+ [{{misc_info_embedded, act}, _}, {{misc_info_embedded, ssl}, _}] =
+ do_get_listener_info(misc_info_embedded),
+ %% Stop embedded act listener and ensure it is gone.
+ ok = embedded_sup:stop_listener(SupPid, {misc_info_embedded, act}),
+ timer:sleep(500),
+ [{{misc_info_embedded, ssl}, _}] =
+ do_get_listener_info(misc_info_embedded),
+ %% Stop embedded ssl listener and ensure it is gone.
+ ok = embedded_sup:stop_listener(SupPid, {misc_info_embedded, ssl}),
+ timer:sleep(500),
+ [] = do_get_listener_info(misc_info_embedded),
+ %% Stop embedded supervisor.
+ embedded_sup:stop(SupPid),
+ ok.
+
+do_get_listener_info(ListenerGroup) ->
+ lists:sort([L || L={{G, _}, _} <- ranch:info(), G=:=ListenerGroup]).
+
%% ssl.
ssl_accept_error(_) ->
diff --git a/test/embedded_sup.erl b/test/embedded_sup.erl
new file mode 100644
index 0000000..52275e0
--- /dev/null
+++ b/test/embedded_sup.erl
@@ -0,0 +1,28 @@
+-module(embedded_sup).
+-behaviour(supervisor).
+-export([init/1]).
+
+-export([start_link/0]).
+-export([stop/1]).
+-export([start_listener/6]).
+-export([stop_listener/2]).
+
+start_link() ->
+ supervisor:start_link(?MODULE, []).
+
+stop(SupPid) ->
+ erlang:exit(SupPid, normal).
+
+init([]) ->
+ {ok, {{one_for_one, 10, 10}, []}}.
+
+start_listener(SupPid, Ref, Transport, TransOpts, Protocol, ProtoOpts) ->
+ supervisor:start_child(
+ SupPid,
+ ranch:child_spec(Ref, Transport, TransOpts, Protocol, ProtoOpts)
+ ).
+
+stop_listener(SupPid, Ref) ->
+ ok = supervisor:terminate_child(SupPid, {ranch_listener_sup, Ref}),
+ ok = supervisor:delete_child(SupPid, {ranch_listener_sup, Ref}),
+ ranch_server:cleanup_listener_opts(Ref).