ranch_server.erl 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. %% Copyright (c) 2012-2018, Loïc Hoguin <essen@ninenines.eu>
  2. %%
  3. %% Permission to use, copy, modify, and/or distribute this software for any
  4. %% purpose with or without fee is hereby granted, provided that the above
  5. %% copyright notice and this permission notice appear in all copies.
  6. %%
  7. %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  10. %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  12. %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  13. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. -module(ranch_server).
  15. -behaviour(gen_server).
  16. %% API.
  17. -export([start_link/0]).
  18. -export([set_new_listener_opts/5]).
  19. -export([cleanup_listener_opts/1]).
  20. -export([cleanup_connections_sups/1]).
  21. -export([set_connections_sup/3]).
  22. -export([get_connections_sup/2]).
  23. -export([get_connections_sups/1]).
  24. -export([get_connections_sups/0]).
  25. -export([set_listener_sup/2]).
  26. -export([get_listener_sup/1]).
  27. -export([get_listener_sups/0]).
  28. -export([set_addr/2]).
  29. -export([get_addr/1]).
  30. -export([set_max_connections/2]).
  31. -export([get_max_connections/1]).
  32. -export([set_transport_options/2]).
  33. -export([get_transport_options/1]).
  34. -export([set_protocol_options/2]).
  35. -export([get_protocol_options/1]).
  36. -export([get_listener_start_args/1]).
  37. -export([count_connections/1]).
  38. %% gen_server.
  39. -export([init/1]).
  40. -export([handle_call/3]).
  41. -export([handle_cast/2]).
  42. -export([handle_info/2]).
  43. -export([terminate/2]).
  44. -export([code_change/3]).
  45. -define(TAB, ?MODULE).
  46. -type monitors() :: [{{reference(), pid()}, any()}].
  47. -record(state, {
  48. monitors = [] :: monitors()
  49. }).
  50. %% API.
  51. -spec start_link() -> {ok, pid()}.
  52. start_link() ->
  53. gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
  54. -spec set_new_listener_opts(ranch:ref(), ranch:max_conns(), any(), any(), [any()]) -> ok.
  55. set_new_listener_opts(Ref, MaxConns, TransOpts, ProtoOpts, StartArgs) ->
  56. gen_server:call(?MODULE, {set_new_listener_opts, Ref, MaxConns, TransOpts, ProtoOpts, StartArgs}).
  57. -spec cleanup_listener_opts(ranch:ref()) -> ok.
  58. cleanup_listener_opts(Ref) ->
  59. _ = ets:delete(?TAB, {addr, Ref}),
  60. _ = ets:delete(?TAB, {max_conns, Ref}),
  61. _ = ets:delete(?TAB, {trans_opts, Ref}),
  62. _ = ets:delete(?TAB, {proto_opts, Ref}),
  63. _ = ets:delete(?TAB, {listener_start_args, Ref}),
  64. %% We also remove the pid of the connection supervisors.
  65. %% Depending on the timing, they might already have been deleted
  66. %% when we handled the monitor DOWN message. However, in some
  67. %% cases when calling stop_listener followed by get_connections_sup,
  68. %% we could end up with the pid still being returned, when we
  69. %% expected a crash (because the listener was stopped).
  70. %% Deleting it explictly here removes any possible confusion.
  71. _ = ets:match_delete(?TAB, {{conns_sup, Ref, '_'}, '_'}),
  72. %% Ditto for the listener supervisor.
  73. _ = ets:delete(?TAB, {listener_sup, Ref}),
  74. ok.
  75. -spec cleanup_connections_sups(ranch:ref()) -> ok.
  76. cleanup_connections_sups(Ref) ->
  77. _ = ets:match_delete(?TAB, {{conns_sup, Ref, '_'}, '_'}),
  78. ok.
  79. -spec set_connections_sup(ranch:ref(), non_neg_integer(), pid()) -> ok.
  80. set_connections_sup(Ref, Id, Pid) ->
  81. gen_server:call(?MODULE, {set_connections_sup, Ref, Id, Pid}).
  82. -spec get_connections_sup(ranch:ref(), pos_integer()) -> pid().
  83. get_connections_sup(Ref, Id) ->
  84. ConnsSups = get_connections_sups(Ref),
  85. NConnsSups = length(ConnsSups),
  86. {_, Pid} = lists:keyfind((Id rem NConnsSups) + 1, 1, ConnsSups),
  87. Pid.
  88. -spec get_connections_sups(ranch:ref()) -> [{pos_integer(), pid()}].
  89. get_connections_sups(Ref) ->
  90. [{Id, Pid} ||
  91. [Id, Pid] <- ets:match(?TAB, {{conns_sup, Ref, '$1'}, '$2'})].
  92. -spec get_connections_sups() -> [{ranch:ref(), pos_integer(), pid()}].
  93. get_connections_sups() ->
  94. [{Ref, Id, Pid} ||
  95. [Ref, Id, Pid] <- ets:match(?TAB, {{conns_sup, '$1', '$2'}, '$3'})].
  96. -spec set_listener_sup(ranch:ref(), pid()) -> ok.
  97. set_listener_sup(Ref, Pid) ->
  98. gen_server:call(?MODULE, {set_listener_sup, Ref, Pid}).
  99. -spec get_listener_sup(ranch:ref()) -> pid().
  100. get_listener_sup(Ref) ->
  101. ets:lookup_element(?TAB, {listener_sup, Ref}, 2).
  102. -spec get_listener_sups() -> [{ranch:ref(), pid()}].
  103. get_listener_sups() ->
  104. [{Ref, Pid} || [Ref, Pid] <- ets:match(?TAB, {{listener_sup, '$1'}, '$2'})].
  105. -spec set_addr(ranch:ref(), {inet:ip_address(), inet:port_number()} |
  106. {local, binary()} | {undefined, undefined}) -> ok.
  107. set_addr(Ref, Addr) ->
  108. gen_server:call(?MODULE, {set_addr, Ref, Addr}).
  109. -spec get_addr(ranch:ref()) -> {inet:ip_address(), inet:port_number()} |
  110. {local, binary()} | {undefined, undefined}.
  111. get_addr(Ref) ->
  112. ets:lookup_element(?TAB, {addr, Ref}, 2).
  113. -spec set_max_connections(ranch:ref(), ranch:max_conns()) -> ok.
  114. set_max_connections(Ref, MaxConnections) ->
  115. gen_server:call(?MODULE, {set_max_conns, Ref, MaxConnections}).
  116. -spec get_max_connections(ranch:ref()) -> ranch:max_conns().
  117. get_max_connections(Ref) ->
  118. ets:lookup_element(?TAB, {max_conns, Ref}, 2).
  119. -spec set_transport_options(ranch:ref(), any()) -> ok.
  120. set_transport_options(Ref, TransOpts) ->
  121. gen_server:call(?MODULE, {set_trans_opts, Ref, TransOpts}).
  122. -spec get_transport_options(ranch:ref()) -> any().
  123. get_transport_options(Ref) ->
  124. ets:lookup_element(?TAB, {trans_opts, Ref}, 2).
  125. -spec set_protocol_options(ranch:ref(), any()) -> ok.
  126. set_protocol_options(Ref, ProtoOpts) ->
  127. gen_server:call(?MODULE, {set_proto_opts, Ref, ProtoOpts}).
  128. -spec get_protocol_options(ranch:ref()) -> any().
  129. get_protocol_options(Ref) ->
  130. ets:lookup_element(?TAB, {proto_opts, Ref}, 2).
  131. -spec get_listener_start_args(ranch:ref()) -> [any()].
  132. get_listener_start_args(Ref) ->
  133. ets:lookup_element(?TAB, {listener_start_args, Ref}, 2).
  134. -spec count_connections(ranch:ref()) -> non_neg_integer().
  135. count_connections(Ref) ->
  136. lists:foldl(
  137. fun ({_, ConnsSup}, Acc) ->
  138. Acc+ranch_conns_sup:active_connections(ConnsSup)
  139. end,
  140. 0,
  141. get_connections_sups(Ref)).
  142. %% gen_server.
  143. init([]) ->
  144. ConnMonitors = [{{erlang:monitor(process, Pid), Pid}, {conns_sup, Ref, Id}} ||
  145. [Ref, Id, Pid] <- ets:match(?TAB, {{conns_sup, '$1', '$2'}, '$3'})],
  146. ListenerMonitors = [{{erlang:monitor(process, Pid), Pid}, {listener_sup, Ref}} ||
  147. [Ref, Pid] <- ets:match(?TAB, {{listener_sup, '$1'}, '$2'})],
  148. {ok, #state{monitors=ConnMonitors++ListenerMonitors}}.
  149. handle_call({set_new_listener_opts, Ref, MaxConns, TransOpts, ProtoOpts, StartArgs}, _, State) ->
  150. ets:insert_new(?TAB, {{max_conns, Ref}, MaxConns}),
  151. ets:insert_new(?TAB, {{trans_opts, Ref}, TransOpts}),
  152. ets:insert_new(?TAB, {{proto_opts, Ref}, ProtoOpts}),
  153. ets:insert_new(?TAB, {{listener_start_args, Ref}, StartArgs}),
  154. {reply, ok, State};
  155. handle_call({set_connections_sup, Ref, Id, Pid}, _, State0) ->
  156. State = set_monitored_process({conns_sup, Ref, Id}, Pid, State0),
  157. {reply, ok, State};
  158. handle_call({set_listener_sup, Ref, Pid}, _, State0) ->
  159. State = set_monitored_process({listener_sup, Ref}, Pid, State0),
  160. {reply, ok, State};
  161. handle_call({set_addr, Ref, Addr}, _, State) ->
  162. true = ets:insert(?TAB, {{addr, Ref}, Addr}),
  163. {reply, ok, State};
  164. handle_call({set_max_conns, Ref, MaxConns}, _, State) ->
  165. ets:insert(?TAB, {{max_conns, Ref}, MaxConns}),
  166. _ = [ConnsSup ! {set_max_conns, MaxConns} || {_, ConnsSup} <- get_connections_sups(Ref)],
  167. {reply, ok, State};
  168. handle_call({set_trans_opts, Ref, Opts}, _, State) ->
  169. ets:insert(?TAB, {{trans_opts, Ref}, Opts}),
  170. {reply, ok, State};
  171. handle_call({set_proto_opts, Ref, Opts}, _, State) ->
  172. ets:insert(?TAB, {{proto_opts, Ref}, Opts}),
  173. _ = [ConnsSup ! {set_opts, Opts} || {_, ConnsSup} <- get_connections_sups(Ref)],
  174. {reply, ok, State};
  175. handle_call(_Request, _From, State) ->
  176. {reply, ignore, State}.
  177. handle_cast(_Request, State) ->
  178. {noreply, State}.
  179. handle_info({'DOWN', MonitorRef, process, Pid, Reason},
  180. State=#state{monitors=Monitors}) ->
  181. {_, TypeRef} = lists:keyfind({MonitorRef, Pid}, 1, Monitors),
  182. ok = case {TypeRef, Reason} of
  183. {{listener_sup, Ref}, normal} ->
  184. cleanup_listener_opts(Ref);
  185. {{listener_sup, Ref}, shutdown} ->
  186. cleanup_listener_opts(Ref);
  187. {{listener_sup, Ref}, {shutdown, _}} ->
  188. cleanup_listener_opts(Ref);
  189. _ ->
  190. _ = ets:delete(?TAB, TypeRef),
  191. ok
  192. end,
  193. Monitors2 = lists:keydelete({MonitorRef, Pid}, 1, Monitors),
  194. {noreply, State#state{monitors=Monitors2}};
  195. handle_info(_Info, State) ->
  196. {noreply, State}.
  197. terminate(_Reason, _State) ->
  198. ok.
  199. code_change(_OldVsn, State, _Extra) ->
  200. {ok, State}.
  201. %% Internal.
  202. set_monitored_process(Key, Pid, State=#state{monitors=Monitors0}) ->
  203. %% First we cleanup the monitor if a residual one exists.
  204. %% This can happen during crashes when the restart is faster
  205. %% than the cleanup.
  206. Monitors = case lists:keytake(Key, 2, Monitors0) of
  207. false ->
  208. Monitors0;
  209. {value, {{OldMonitorRef, _}, _}, Monitors1} ->
  210. true = erlang:demonitor(OldMonitorRef, [flush]),
  211. Monitors1
  212. end,
  213. %% Then we unconditionally insert in the ets table.
  214. %% If residual data is there, it will be overwritten.
  215. true = ets:insert(?TAB, {Key, Pid}),
  216. %% Finally we start monitoring this new process.
  217. MonitorRef = erlang:monitor(process, Pid),
  218. State#state{monitors=[{{MonitorRef, Pid}, Key}|Monitors]}.