ranch_server.erl 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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([set_connections_sup/2]).
  21. -export([get_connections_sup/1]).
  22. -export([get_connections_sups/0]).
  23. -export([set_listener_sup/2]).
  24. -export([get_listener_sup/1]).
  25. -export([get_listener_sups/0]).
  26. -export([set_addr/2]).
  27. -export([get_addr/1]).
  28. -export([set_max_connections/2]).
  29. -export([get_max_connections/1]).
  30. -export([set_transport_options/2]).
  31. -export([get_transport_options/1]).
  32. -export([set_protocol_options/2]).
  33. -export([get_protocol_options/1]).
  34. -export([get_listener_start_args/1]).
  35. -export([count_connections/1]).
  36. %% gen_server.
  37. -export([init/1]).
  38. -export([handle_call/3]).
  39. -export([handle_cast/2]).
  40. -export([handle_info/2]).
  41. -export([terminate/2]).
  42. -export([code_change/3]).
  43. -define(TAB, ?MODULE).
  44. -type monitors() :: [{{reference(), pid()}, any()}].
  45. -record(state, {
  46. monitors = [] :: monitors()
  47. }).
  48. %% API.
  49. -spec start_link() -> {ok, pid()}.
  50. start_link() ->
  51. gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
  52. -spec set_new_listener_opts(ranch:ref(), ranch:max_conns(), any(), any(), [any()]) -> ok.
  53. set_new_listener_opts(Ref, MaxConns, TransOpts, ProtoOpts, StartArgs) ->
  54. gen_server:call(?MODULE, {set_new_listener_opts, Ref, MaxConns, TransOpts, ProtoOpts, StartArgs}).
  55. -spec cleanup_listener_opts(ranch:ref()) -> ok.
  56. cleanup_listener_opts(Ref) ->
  57. _ = ets:delete(?TAB, {addr, Ref}),
  58. _ = ets:delete(?TAB, {max_conns, Ref}),
  59. _ = ets:delete(?TAB, {trans_opts, Ref}),
  60. _ = ets:delete(?TAB, {proto_opts, Ref}),
  61. _ = ets:delete(?TAB, {listener_start_args, Ref}),
  62. %% We also remove the pid of the connections supervisor.
  63. %% Depending on the timing, it might already have been deleted
  64. %% when we handled the monitor DOWN message. However, in some
  65. %% cases when calling stop_listener followed by get_connections_sup,
  66. %% we could end up with the pid still being returned, when we
  67. %% expected a crash (because the listener was stopped).
  68. %% Deleting it explictly here removes any possible confusion.
  69. _ = ets:delete(?TAB, {conns_sup, Ref}),
  70. %% Ditto for the listener supervisor.
  71. _ = ets:delete(?TAB, {listener_sup, Ref}),
  72. ok.
  73. -spec set_connections_sup(ranch:ref(), pid()) -> ok.
  74. set_connections_sup(Ref, Pid) ->
  75. gen_server:call(?MODULE, {set_connections_sup, Ref, Pid}).
  76. -spec get_connections_sup(ranch:ref()) -> pid().
  77. get_connections_sup(Ref) ->
  78. ets:lookup_element(?TAB, {conns_sup, Ref}, 2).
  79. -spec get_connections_sups() -> [{ranch:ref(), pid()}].
  80. get_connections_sups() ->
  81. [{Ref, Pid} || [Ref, Pid] <- ets:match(?TAB, {{conns_sup, '$1'}, '$2'})].
  82. -spec set_listener_sup(ranch:ref(), pid()) -> ok.
  83. set_listener_sup(Ref, Pid) ->
  84. gen_server:call(?MODULE, {set_listener_sup, Ref, Pid}).
  85. -spec get_listener_sup(ranch:ref()) -> pid().
  86. get_listener_sup(Ref) ->
  87. ets:lookup_element(?TAB, {listener_sup, Ref}, 2).
  88. -spec get_listener_sups() -> [{ranch:ref(), pid()}].
  89. get_listener_sups() ->
  90. [{Ref, Pid} || [Ref, Pid] <- ets:match(?TAB, {{listener_sup, '$1'}, '$2'})].
  91. -spec set_addr(ranch:ref(), {inet:ip_address(), inet:port_number()} | {undefined, undefined}) -> ok.
  92. set_addr(Ref, Addr) ->
  93. gen_server:call(?MODULE, {set_addr, Ref, Addr}).
  94. -spec get_addr(ranch:ref()) -> {inet:ip_address(), inet:port_number()} | {undefined, undefined}.
  95. get_addr(Ref) ->
  96. ets:lookup_element(?TAB, {addr, Ref}, 2).
  97. -spec set_max_connections(ranch:ref(), ranch:max_conns()) -> ok.
  98. set_max_connections(Ref, MaxConnections) ->
  99. gen_server:call(?MODULE, {set_max_conns, Ref, MaxConnections}).
  100. -spec get_max_connections(ranch:ref()) -> ranch:max_conns().
  101. get_max_connections(Ref) ->
  102. ets:lookup_element(?TAB, {max_conns, Ref}, 2).
  103. -spec set_transport_options(ranch:ref(), any()) -> ok.
  104. set_transport_options(Ref, TransOpts) ->
  105. gen_server:call(?MODULE, {set_trans_opts, Ref, TransOpts}).
  106. -spec get_transport_options(ranch:ref()) -> any().
  107. get_transport_options(Ref) ->
  108. ets:lookup_element(?TAB, {trans_opts, Ref}, 2).
  109. -spec set_protocol_options(ranch:ref(), any()) -> ok.
  110. set_protocol_options(Ref, ProtoOpts) ->
  111. gen_server:call(?MODULE, {set_proto_opts, Ref, ProtoOpts}).
  112. -spec get_protocol_options(ranch:ref()) -> any().
  113. get_protocol_options(Ref) ->
  114. ets:lookup_element(?TAB, {proto_opts, Ref}, 2).
  115. -spec get_listener_start_args(ranch:ref()) -> [any()].
  116. get_listener_start_args(Ref) ->
  117. ets:lookup_element(?TAB, {listener_start_args, Ref}, 2).
  118. -spec count_connections(ranch:ref()) -> non_neg_integer().
  119. count_connections(Ref) ->
  120. ranch_conns_sup:active_connections(get_connections_sup(Ref)).
  121. %% gen_server.
  122. init([]) ->
  123. ConnMonitors = [{{erlang:monitor(process, Pid), Pid}, {conns_sup, Ref}} ||
  124. [Ref, Pid] <- ets:match(?TAB, {{conns_sup, '$1'}, '$2'})],
  125. ListenerMonitors = [{{erlang:monitor(process, Pid), Pid}, {listener_sup, Ref}} ||
  126. [Ref, Pid] <- ets:match(?TAB, {{listener_sup, '$1'}, '$2'})],
  127. {ok, #state{monitors=ConnMonitors++ListenerMonitors}}.
  128. handle_call({set_new_listener_opts, Ref, MaxConns, TransOpts, ProtoOpts, StartArgs}, _, State) ->
  129. ets:insert_new(?TAB, {{max_conns, Ref}, MaxConns}),
  130. ets:insert_new(?TAB, {{trans_opts, Ref}, TransOpts}),
  131. ets:insert_new(?TAB, {{proto_opts, Ref}, ProtoOpts}),
  132. ets:insert_new(?TAB, {{listener_start_args, Ref}, StartArgs}),
  133. {reply, ok, State};
  134. handle_call({set_connections_sup, Ref, Pid}, _, State0) ->
  135. State = set_monitored_process({conns_sup, Ref}, Pid, State0),
  136. {reply, ok, State};
  137. handle_call({set_listener_sup, Ref, Pid}, _, State0) ->
  138. State = set_monitored_process({listener_sup, Ref}, Pid, State0),
  139. {reply, ok, State};
  140. handle_call({set_addr, Ref, Addr}, _, State) ->
  141. true = ets:insert(?TAB, {{addr, Ref}, Addr}),
  142. {reply, ok, State};
  143. handle_call({set_max_conns, Ref, MaxConns}, _, State) ->
  144. ets:insert(?TAB, {{max_conns, Ref}, MaxConns}),
  145. ConnsSup = get_connections_sup(Ref),
  146. ConnsSup ! {set_max_conns, MaxConns},
  147. {reply, ok, State};
  148. handle_call({set_trans_opts, Ref, Opts}, _, State) ->
  149. ets:insert(?TAB, {{trans_opts, Ref}, Opts}),
  150. {reply, ok, State};
  151. handle_call({set_proto_opts, Ref, Opts}, _, State) ->
  152. ets:insert(?TAB, {{proto_opts, Ref}, Opts}),
  153. ConnsSup = get_connections_sup(Ref),
  154. ConnsSup ! {set_opts, Opts},
  155. {reply, ok, State};
  156. handle_call(_Request, _From, State) ->
  157. {reply, ignore, State}.
  158. handle_cast(_Request, State) ->
  159. {noreply, State}.
  160. handle_info({'DOWN', MonitorRef, process, Pid, Reason},
  161. State=#state{monitors=Monitors}) ->
  162. {_, TypeRef} = lists:keyfind({MonitorRef, Pid}, 1, Monitors),
  163. ok = case {TypeRef, Reason} of
  164. {{listener_sup, Ref}, normal} ->
  165. cleanup_listener_opts(Ref);
  166. {{listener_sup, Ref}, shutdown} ->
  167. cleanup_listener_opts(Ref);
  168. {{listener_sup, Ref}, {shutdown, _}} ->
  169. cleanup_listener_opts(Ref);
  170. _ ->
  171. _ = ets:delete(?TAB, TypeRef),
  172. ok
  173. end,
  174. Monitors2 = lists:keydelete({MonitorRef, Pid}, 1, Monitors),
  175. {noreply, State#state{monitors=Monitors2}};
  176. handle_info(_Info, State) ->
  177. {noreply, State}.
  178. terminate(_Reason, _State) ->
  179. ok.
  180. code_change(_OldVsn, State, _Extra) ->
  181. {ok, State}.
  182. %% Internal.
  183. set_monitored_process(Key, Pid, State=#state{monitors=Monitors0}) ->
  184. %% First we cleanup the monitor if a residual one exists.
  185. %% This can happen during crashes when the restart is faster
  186. %% than the cleanup.
  187. Monitors = case lists:keytake(Key, 2, Monitors0) of
  188. false ->
  189. Monitors0;
  190. {value, {{OldMonitorRef, _}, _}, Monitors1} ->
  191. true = erlang:demonitor(OldMonitorRef, [flush]),
  192. Monitors1
  193. end,
  194. %% Then we unconditionally insert in the ets table.
  195. %% If residual data is there, it will be overwritten.
  196. true = ets:insert(?TAB, {Key, Pid}),
  197. %% Finally we start monitoring this new process.
  198. MonitorRef = erlang:monitor(process, Pid),
  199. State#state{monitors=[{{MonitorRef, Pid}, Key}|Monitors]}.