From fa608621bfedbb981657d2d09e2b321dddc0ad8e Mon Sep 17 00:00:00 2001 From: "j.uhlig" Date: Tue, 13 Mar 2018 09:31:20 +0100 Subject: Fix ranch:info/0 and ranch:procs/2 in embedded mode --- src/ranch.erl | 26 ++---------- src/ranch_listener_sup.erl | 4 +- src/ranch_server.erl | 64 ++++++++++++++++++++++++----- test/acceptor_SUITE.erl | 100 ++++++++++++++++++++++++++++++++++++++++++++- test/embedded_sup.erl | 28 +++++++++++++ 5 files changed, 185 insertions(+), 37 deletions(-) create mode 100644 test/embedded_sup.erl 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). -- cgit v1.2.3