Browse Source

Fix ranch:info/0 and ranch:procs/2 in embedded mode

j.uhlig 7 years ago
parent
commit
fa608621bf
5 changed files with 185 additions and 37 deletions
  1. 3 23
      src/ranch.erl
  2. 3 1
      src/ranch_listener_sup.erl
  3. 53 11
      src/ranch_server.erl
  4. 98 2
      test/acceptor_SUITE.erl
  5. 28 0
      test/embedded_sup.erl

+ 3 - 23
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)].

+ 3 - 1
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),

+ 53 - 11
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) ->

+ 98 - 2
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(_) ->

+ 28 - 0
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).