You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

625 lines
19 KiB

  1. %% Copyright (c) 2011-2021, Loïc Hoguin <essen@ninenines.eu>
  2. %% Copyright (c) 2020-2021, Jan Uhlig <juhlig@hnc-agency.org>
  3. %% Copyright (c) 2021, Maria Scott <maria-12648430@hnc-agency.org>
  4. %%
  5. %% Permission to use, copy, modify, and/or distribute this software for any
  6. %% purpose with or without fee is hereby granted, provided that the above
  7. %% copyright notice and this permission notice appear in all copies.
  8. %%
  9. %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14. %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. -module(ranch).
  17. -export([start_listener/5]).
  18. -export([normalize_opts/1]).
  19. -export([stop_listener/1]).
  20. -export([suspend_listener/1]).
  21. -export([resume_listener/1]).
  22. -export([stop_all_acceptors/0]).
  23. -export([restart_all_acceptors/0]).
  24. -export([child_spec/5]).
  25. -export([handshake/1]).
  26. -export([handshake/2]).
  27. -export([handshake_continue/1]).
  28. -export([handshake_continue/2]).
  29. -export([handshake_cancel/1]).
  30. -export([recv_proxy_header/2]).
  31. -export([remove_connection/1]).
  32. -export([get_status/1]).
  33. -export([get_addr/1]).
  34. -export([get_port/1]).
  35. -export([get_max_connections/1]).
  36. -export([set_max_connections/2]).
  37. -export([get_transport_options/1]).
  38. -export([set_transport_options/2]).
  39. -export([get_protocol_options/1]).
  40. -export([set_protocol_options/2]).
  41. -export([info/0]).
  42. -export([info/1]).
  43. -export([procs/2]).
  44. -export([wait_for_connections/3]).
  45. -export([wait_for_connections/4]).
  46. -export([filter_options/4]).
  47. -export([set_option_default/3]).
  48. -export([require/1]).
  49. -export([log/4]).
  50. -type max_conns() :: non_neg_integer() | infinity.
  51. -export_type([max_conns/0]).
  52. -type opts() :: any() | transport_opts(any()).
  53. -export_type([opts/0]).
  54. -type alarm(Type, Callback) :: #{
  55. type := Type,
  56. callback := Callback,
  57. treshold := non_neg_integer(),
  58. cooldown => non_neg_integer()
  59. }.
  60. -type alarm_num_connections() :: alarm(num_connections, fun((ref(), term(), pid(), [pid()]) -> any())).
  61. -type transport_opts(SocketOpts) :: #{
  62. alarms => #{term() => alarm_num_connections()},
  63. connection_type => worker | supervisor,
  64. handshake_timeout => timeout(),
  65. logger => module(),
  66. max_connections => max_conns(),
  67. num_acceptors => pos_integer(),
  68. num_conns_sups => pos_integer(),
  69. num_listen_sockets => pos_integer(),
  70. post_listen_callback => fun((term()) -> ok | {error, term()}),
  71. shutdown => timeout() | brutal_kill,
  72. socket_opts => SocketOpts
  73. }.
  74. -export_type([transport_opts/1]).
  75. -type ref() :: any().
  76. -export_type([ref/0]).
  77. -spec start_listener(ref(), module(), opts(), module(), any())
  78. -> supervisor:startchild_ret().
  79. start_listener(Ref, Transport, TransOpts0, Protocol, ProtoOpts)
  80. when is_atom(Transport), is_atom(Protocol) ->
  81. TransOpts = normalize_opts(TransOpts0),
  82. _ = code:ensure_loaded(Transport),
  83. case {erlang:function_exported(Transport, name, 0), validate_transport_opts(TransOpts)} of
  84. {true, ok} ->
  85. ChildSpec = #{id => {ranch_listener_sup, Ref}, start => {ranch_listener_sup, start_link, [
  86. Ref, Transport, TransOpts, Protocol, ProtoOpts
  87. ]}, type => supervisor},
  88. maybe_started(supervisor:start_child(ranch_sup, ChildSpec));
  89. {false, _} ->
  90. {error, {bad_transport, Transport}};
  91. {_, TransOptsError} ->
  92. TransOptsError
  93. end.
  94. -spec normalize_opts(opts()) -> transport_opts(any()).
  95. normalize_opts(Map) when is_map(Map) ->
  96. Map;
  97. normalize_opts(Any) ->
  98. #{socket_opts => Any}.
  99. -spec validate_transport_opts(transport_opts(any())) -> ok | {error, any()}.
  100. validate_transport_opts(Opts) ->
  101. maps:fold(fun
  102. (Key, Value, ok) ->
  103. case validate_transport_opt(Key, Value, Opts) of
  104. true ->
  105. ok;
  106. false ->
  107. {error, {bad_option, Key}}
  108. end;
  109. (_, _, Acc) ->
  110. Acc
  111. end, ok, Opts).
  112. -spec validate_transport_opt(any(), any(), transport_opts(any())) -> boolean().
  113. validate_transport_opt(connection_type, worker, _) ->
  114. true;
  115. validate_transport_opt(connection_type, supervisor, _) ->
  116. true;
  117. validate_transport_opt(handshake_timeout, infinity, _) ->
  118. true;
  119. validate_transport_opt(handshake_timeout, Value, _) ->
  120. is_integer(Value) andalso Value >= 0;
  121. validate_transport_opt(max_connections, infinity, _) ->
  122. true;
  123. validate_transport_opt(max_connections, Value, _) ->
  124. is_integer(Value) andalso Value >= 0;
  125. validate_transport_opt(alarms, Alarms, _) ->
  126. maps:fold(
  127. fun
  128. (_, Opts, true) ->
  129. validate_alarm(Opts);
  130. (_, _, false) ->
  131. false
  132. end,
  133. true,
  134. Alarms);
  135. validate_transport_opt(logger, Value, _) ->
  136. is_atom(Value);
  137. validate_transport_opt(num_acceptors, Value, _) ->
  138. is_integer(Value) andalso Value > 0;
  139. validate_transport_opt(num_conns_sups, Value, _) ->
  140. is_integer(Value) andalso Value > 0;
  141. validate_transport_opt(num_listen_sockets, Value, Opts) ->
  142. is_integer(Value) andalso Value > 0
  143. andalso Value =< maps:get(num_acceptors, Opts, 10);
  144. validate_transport_opt(post_listen_callback, Value, _) ->
  145. is_function(Value, 1);
  146. validate_transport_opt(shutdown, brutal_kill, _) ->
  147. true;
  148. validate_transport_opt(shutdown, infinity, _) ->
  149. true;
  150. validate_transport_opt(shutdown, Value, _) ->
  151. is_integer(Value) andalso Value >= 0;
  152. validate_transport_opt(socket_opts, _, _) ->
  153. true;
  154. validate_transport_opt(_, _, _) ->
  155. false.
  156. validate_alarm(Alarm = #{type := num_connections, treshold := Treshold,
  157. callback := Callback}) ->
  158. is_integer(Treshold) andalso Treshold >= 0
  159. andalso is_function(Callback, 4)
  160. andalso case Alarm of
  161. #{cooldown := Cooldown} ->
  162. is_integer(Cooldown) andalso Cooldown >= 0;
  163. _ ->
  164. true
  165. end;
  166. validate_alarm(_) ->
  167. false.
  168. maybe_started({error, {{shutdown,
  169. {failed_to_start_child, ranch_acceptors_sup,
  170. {listen_error, _, Reason}}}, _}} = Error) ->
  171. start_error(Reason, Error);
  172. maybe_started(Res) ->
  173. Res.
  174. start_error(E=eaddrinuse, _) -> {error, E};
  175. start_error(E=eacces, _) -> {error, E};
  176. start_error(E=no_cert, _) -> {error, E};
  177. start_error(_, Error) -> Error.
  178. -spec stop_listener(ref()) -> ok | {error, not_found}.
  179. stop_listener(Ref) ->
  180. [_, Transport, _, _, _] = ranch_server:get_listener_start_args(Ref),
  181. TransOpts = get_transport_options(Ref),
  182. case supervisor:terminate_child(ranch_sup, {ranch_listener_sup, Ref}) of
  183. ok ->
  184. _ = supervisor:delete_child(ranch_sup, {ranch_listener_sup, Ref}),
  185. ranch_server:cleanup_listener_opts(Ref),
  186. Transport:cleanup(TransOpts);
  187. {error, Reason} ->
  188. {error, Reason}
  189. end.
  190. -spec suspend_listener(ref()) -> ok | {error, any()}.
  191. suspend_listener(Ref) ->
  192. case get_status(Ref) of
  193. running ->
  194. ListenerSup = ranch_server:get_listener_sup(Ref),
  195. ok = ranch_server:set_addr(Ref, {undefined, undefined}),
  196. supervisor:terminate_child(ListenerSup, ranch_acceptors_sup);
  197. suspended ->
  198. ok
  199. end.
  200. -spec resume_listener(ref()) -> ok | {error, any()}.
  201. resume_listener(Ref) ->
  202. case get_status(Ref) of
  203. running ->
  204. ok;
  205. suspended ->
  206. ListenerSup = ranch_server:get_listener_sup(Ref),
  207. Res = supervisor:restart_child(ListenerSup, ranch_acceptors_sup),
  208. maybe_resumed(Res)
  209. end.
  210. maybe_resumed(Error={error, {listen_error, _, Reason}}) ->
  211. start_error(Reason, Error);
  212. maybe_resumed({ok, _}) ->
  213. ok;
  214. maybe_resumed({ok, _, _}) ->
  215. ok;
  216. maybe_resumed(Res) ->
  217. Res.
  218. -spec stop_all_acceptors() -> ok.
  219. stop_all_acceptors() ->
  220. _ = [ok = do_acceptors(Pid, terminate_child)
  221. || {_, Pid} <- ranch_server:get_listener_sups()],
  222. ok.
  223. -spec restart_all_acceptors() -> ok.
  224. restart_all_acceptors() ->
  225. _ = [ok = do_acceptors(Pid, restart_child)
  226. || {_, Pid} <- ranch_server:get_listener_sups()],
  227. ok.
  228. do_acceptors(ListenerSup, F) ->
  229. ListenerChildren = supervisor:which_children(ListenerSup),
  230. case lists:keyfind(ranch_acceptors_sup, 1, ListenerChildren) of
  231. {_, AcceptorsSup, _, _} when is_pid(AcceptorsSup) ->
  232. AcceptorChildren = supervisor:which_children(AcceptorsSup),
  233. %% @todo What about errors?
  234. _ = [supervisor:F(AcceptorsSup, AcceptorId)
  235. || {AcceptorId, _, _, _} <- AcceptorChildren],
  236. ok;
  237. {_, Atom, _, _} ->
  238. {error, Atom}
  239. end.
  240. -spec child_spec(ref(), module(), opts(), module(), any())
  241. -> supervisor:child_spec().
  242. child_spec(Ref, Transport, TransOpts0, Protocol, ProtoOpts) ->
  243. TransOpts = normalize_opts(TransOpts0),
  244. #{id => {ranch_embedded_sup, Ref}, start => {ranch_embedded_sup, start_link, [
  245. Ref, Transport, TransOpts, Protocol, ProtoOpts
  246. ]}, type => supervisor}.
  247. -spec handshake(ref()) -> {ok, ranch_transport:socket()} | {continue, any()}.
  248. handshake(Ref) ->
  249. handshake1(Ref, undefined).
  250. -spec handshake(ref(), any()) -> {ok, ranch_transport:socket()} | {continue, any()}.
  251. handshake(Ref, Opts) ->
  252. handshake1(Ref, {opts, Opts}).
  253. handshake1(Ref, Opts) ->
  254. receive {handshake, Ref, Transport, CSocket, Timeout} ->
  255. Handshake = handshake_transport(Transport, handshake, CSocket, Opts, Timeout),
  256. handshake_result(Handshake, Ref, Transport, CSocket, Timeout)
  257. end.
  258. -spec handshake_continue(ref()) -> {ok, ranch_transport:socket()}.
  259. handshake_continue(Ref) ->
  260. handshake_continue1(Ref, undefined).
  261. -spec handshake_continue(ref(), any()) -> {ok, ranch_transport:socket()}.
  262. handshake_continue(Ref, Opts) ->
  263. handshake_continue1(Ref, {opts, Opts}).
  264. handshake_continue1(Ref, Opts) ->
  265. receive {handshake_continue, Ref, Transport, CSocket, Timeout} ->
  266. Handshake = handshake_transport(Transport, handshake_continue, CSocket, Opts, Timeout),
  267. handshake_result(Handshake, Ref, Transport, CSocket, Timeout)
  268. end.
  269. handshake_transport(Transport, Fun, CSocket, undefined, Timeout) ->
  270. Transport:Fun(CSocket, Timeout);
  271. handshake_transport(Transport, Fun, CSocket, {opts, Opts}, Timeout) ->
  272. Transport:Fun(CSocket, Opts, Timeout).
  273. handshake_result(Result, Ref, Transport, CSocket, Timeout) ->
  274. case Result of
  275. OK = {ok, _} ->
  276. OK;
  277. {ok, CSocket2, Info} ->
  278. self() ! {handshake_continue, Ref, Transport, CSocket2, Timeout},
  279. {continue, Info};
  280. {error, {tls_alert, _}} ->
  281. ok = Transport:close(CSocket),
  282. exit(normal);
  283. {error, Reason} when Reason =:= timeout; Reason =:= closed ->
  284. ok = Transport:close(CSocket),
  285. exit(normal);
  286. {error, Reason} ->
  287. ok = Transport:close(CSocket),
  288. error(Reason)
  289. end.
  290. -spec handshake_cancel(ref()) -> ok.
  291. handshake_cancel(Ref) ->
  292. receive {handshake_continue, Ref, Transport, CSocket, _} ->
  293. Transport:handshake_cancel(CSocket)
  294. end.
  295. %% Unlike handshake/2 this function always return errors because
  296. %% the communication between the proxy and the server are expected
  297. %% to be reliable. If there is a problem while receiving the proxy
  298. %% header, we probably want to know about it.
  299. -spec recv_proxy_header(ref(), timeout())
  300. -> {ok, ranch_proxy_header:proxy_info()}
  301. | {error, closed | atom()}
  302. | {error, protocol_error, atom()}.
  303. recv_proxy_header(Ref, Timeout) ->
  304. receive HandshakeState={handshake, Ref, Transport, CSocket, _} ->
  305. self() ! HandshakeState,
  306. Transport:recv_proxy_header(CSocket, Timeout)
  307. end.
  308. -spec remove_connection(ref()) -> ok.
  309. remove_connection(Ref) ->
  310. ListenerSup = ranch_server:get_listener_sup(Ref),
  311. {_, ConnsSupSup, _, _} = lists:keyfind(ranch_conns_sup_sup, 1,
  312. supervisor:which_children(ListenerSup)),
  313. _ = [ConnsSup ! {remove_connection, Ref, self()} ||
  314. {_, ConnsSup, _, _} <- supervisor:which_children(ConnsSupSup)],
  315. ok.
  316. -spec get_status(ref()) -> running | suspended.
  317. get_status(Ref) ->
  318. ListenerSup = ranch_server:get_listener_sup(Ref),
  319. Children = supervisor:which_children(ListenerSup),
  320. case lists:keyfind(ranch_acceptors_sup, 1, Children) of
  321. {_, undefined, _, _} ->
  322. suspended;
  323. _ ->
  324. running
  325. end.
  326. -spec get_addr(ref()) -> {inet:ip_address(), inet:port_number()} |
  327. {local, binary()} | {undefined, undefined}.
  328. get_addr(Ref) ->
  329. ranch_server:get_addr(Ref).
  330. -spec get_port(ref()) -> inet:port_number() | undefined.
  331. get_port(Ref) ->
  332. case get_addr(Ref) of
  333. {local, _} ->
  334. undefined;
  335. {_, Port} ->
  336. Port
  337. end.
  338. -spec get_connections(ref(), active|all) -> non_neg_integer().
  339. get_connections(Ref, active) ->
  340. SupCounts = [ranch_conns_sup:active_connections(ConnsSup) ||
  341. {_, ConnsSup} <- ranch_server:get_connections_sups(Ref)],
  342. lists:sum(SupCounts);
  343. get_connections(Ref, all) ->
  344. SupCounts = [proplists:get_value(active, supervisor:count_children(ConnsSup)) ||
  345. {_, ConnsSup} <- ranch_server:get_connections_sups(Ref)],
  346. lists:sum(SupCounts).
  347. -spec get_max_connections(ref()) -> max_conns().
  348. get_max_connections(Ref) ->
  349. ranch_server:get_max_connections(Ref).
  350. -spec set_max_connections(ref(), max_conns()) -> ok.
  351. set_max_connections(Ref, MaxConnections) ->
  352. ranch_server:set_max_connections(Ref, MaxConnections).
  353. -spec get_transport_options(ref()) -> transport_opts(any()).
  354. get_transport_options(Ref) ->
  355. ranch_server:get_transport_options(Ref).
  356. -spec set_transport_options(ref(), opts()) -> ok | {error, term()}.
  357. set_transport_options(Ref, TransOpts0) ->
  358. TransOpts = normalize_opts(TransOpts0),
  359. case validate_transport_opts(TransOpts) of
  360. ok ->
  361. ok = ranch_server:set_transport_options(Ref, TransOpts),
  362. ok = apply_transport_options(Ref, TransOpts);
  363. TransOptsError ->
  364. TransOptsError
  365. end.
  366. apply_transport_options(Ref, TransOpts) ->
  367. _ = [ConnsSup ! {set_transport_options, TransOpts}
  368. || {_, ConnsSup} <- ranch_server:get_connections_sups(Ref)],
  369. ok.
  370. -spec get_protocol_options(ref()) -> any().
  371. get_protocol_options(Ref) ->
  372. ranch_server:get_protocol_options(Ref).
  373. -spec set_protocol_options(ref(), any()) -> ok.
  374. set_protocol_options(Ref, Opts) ->
  375. ranch_server:set_protocol_options(Ref, Opts).
  376. -spec info() -> #{ref() := #{atom() := term()}}.
  377. info() ->
  378. lists:foldl(
  379. fun ({Ref, Pid}, Acc) ->
  380. Acc#{Ref => listener_info(Ref, Pid)}
  381. end,
  382. #{},
  383. ranch_server:get_listener_sups()
  384. ).
  385. -spec info(ref()) -> #{atom() := term()}.
  386. info(Ref) ->
  387. Pid = ranch_server:get_listener_sup(Ref),
  388. listener_info(Ref, Pid).
  389. listener_info(Ref, Pid) ->
  390. [_, Transport, _, Protocol, _] = ranch_server:get_listener_start_args(Ref),
  391. Status = get_status(Ref),
  392. {IP, Port} = case get_addr(Ref) of
  393. Addr = {local, _} ->
  394. {Addr, undefined};
  395. Addr ->
  396. Addr
  397. end,
  398. MaxConns = get_max_connections(Ref),
  399. TransOpts = ranch_server:get_transport_options(Ref),
  400. ProtoOpts = get_protocol_options(Ref),
  401. #{
  402. pid => Pid,
  403. status => Status,
  404. ip => IP,
  405. port => Port,
  406. max_connections => MaxConns,
  407. active_connections => get_connections(Ref, active),
  408. all_connections => get_connections(Ref, all),
  409. transport => Transport,
  410. transport_options => TransOpts,
  411. protocol => Protocol,
  412. protocol_options => ProtoOpts,
  413. metrics => metrics(Ref)
  414. }.
  415. -spec procs(ref(), acceptors | connections) -> [pid()].
  416. procs(Ref, Type) ->
  417. ListenerSup = ranch_server:get_listener_sup(Ref),
  418. procs1(ListenerSup, Type).
  419. procs1(ListenerSup, acceptors) ->
  420. {_, SupPid, _, _} = lists:keyfind(ranch_acceptors_sup, 1,
  421. supervisor:which_children(ListenerSup)),
  422. try
  423. [Pid || {_, Pid, _, _} <- supervisor:which_children(SupPid)]
  424. catch exit:{noproc, _} ->
  425. []
  426. end;
  427. procs1(ListenerSup, connections) ->
  428. {_, SupSupPid, _, _} = lists:keyfind(ranch_conns_sup_sup, 1,
  429. supervisor:which_children(ListenerSup)),
  430. Conns=
  431. lists:map(fun ({_, SupPid, _, _}) ->
  432. [Pid || {_, Pid, _, _} <- supervisor:which_children(SupPid)]
  433. end,
  434. supervisor:which_children(SupSupPid)
  435. ),
  436. lists:flatten(Conns).
  437. -spec metrics(ref()) -> #{}.
  438. metrics(Ref) ->
  439. Counters = ranch_server:get_stats_counters(Ref),
  440. CounterInfo = counters:info(Counters),
  441. NumCounters = maps:get(size, CounterInfo),
  442. NumConnsSups = NumCounters div 2,
  443. lists:foldl(
  444. fun (Id, Acc) ->
  445. Acc#{
  446. {conns_sup, Id, accept} => counters:get(Counters, 2*Id-1),
  447. {conns_sup, Id, terminate} => counters:get(Counters, 2*Id)
  448. }
  449. end,
  450. #{},
  451. lists:seq(1, NumConnsSups)
  452. ).
  453. -spec wait_for_connections
  454. (ref(), '>' | '>=' | '==' | '=<', non_neg_integer()) -> ok;
  455. (ref(), '<', pos_integer()) -> ok.
  456. wait_for_connections(Ref, Op, NumConns) ->
  457. wait_for_connections(Ref, Op, NumConns, 1000).
  458. -spec wait_for_connections
  459. (ref(), '>' | '>=' | '==' | '=<', non_neg_integer(), non_neg_integer()) -> ok;
  460. (ref(), '<', pos_integer(), non_neg_integer()) -> ok.
  461. wait_for_connections(Ref, Op, NumConns, Interval) ->
  462. validate_op(Op, NumConns),
  463. validate_num_conns(NumConns),
  464. validate_interval(Interval),
  465. wait_for_connections_loop(Ref, Op, NumConns, Interval).
  466. validate_op('>', _) -> ok;
  467. validate_op('>=', _) -> ok;
  468. validate_op('==', _) -> ok;
  469. validate_op('=<', _) -> ok;
  470. validate_op('<', NumConns) when NumConns > 0 -> ok;
  471. validate_op(_, _) -> error(badarg).
  472. validate_num_conns(NumConns) when is_integer(NumConns), NumConns >= 0 -> ok;
  473. validate_num_conns(_) -> error(badarg).
  474. validate_interval(Interval) when is_integer(Interval), Interval >= 0 -> ok;
  475. validate_interval(_) -> error(badarg).
  476. wait_for_connections_loop(Ref, Op, NumConns, Interval) ->
  477. CurConns = try
  478. get_connections(Ref, all)
  479. catch _:_ ->
  480. 0
  481. end,
  482. case erlang:Op(CurConns, NumConns) of
  483. true ->
  484. ok;
  485. false when Interval =:= 0 ->
  486. wait_for_connections_loop(Ref, Op, NumConns, Interval);
  487. false ->
  488. timer:sleep(Interval),
  489. wait_for_connections_loop(Ref, Op, NumConns, Interval)
  490. end.
  491. -spec filter_options([inet | inet6 | {atom(), any()} | {raw, any(), any(), any()}],
  492. [atom()], Acc, module()) -> Acc when Acc :: [any()].
  493. filter_options(UserOptions, DisallowedKeys, DefaultOptions, Logger) ->
  494. AllowedOptions = filter_user_options(UserOptions, DisallowedKeys, Logger),
  495. lists:foldl(fun merge_options/2, DefaultOptions, AllowedOptions).
  496. %% 2-tuple options.
  497. filter_user_options([Opt = {Key, _}|Tail], DisallowedKeys, Logger) ->
  498. case lists:member(Key, DisallowedKeys) of
  499. false ->
  500. [Opt|filter_user_options(Tail, DisallowedKeys, Logger)];
  501. true ->
  502. filter_options_warning(Opt, Logger),
  503. filter_user_options(Tail, DisallowedKeys, Logger)
  504. end;
  505. %% Special option forms.
  506. filter_user_options([inet|Tail], DisallowedKeys, Logger) ->
  507. [inet|filter_user_options(Tail, DisallowedKeys, Logger)];
  508. filter_user_options([inet6|Tail], DisallowedKeys, Logger) ->
  509. [inet6|filter_user_options(Tail, DisallowedKeys, Logger)];
  510. filter_user_options([Opt = {raw, _, _, _}|Tail], DisallowedKeys, Logger) ->
  511. [Opt|filter_user_options(Tail, DisallowedKeys, Logger)];
  512. filter_user_options([Opt|Tail], DisallowedKeys, Logger) ->
  513. filter_options_warning(Opt, Logger),
  514. filter_user_options(Tail, DisallowedKeys, Logger);
  515. filter_user_options([], _, _) ->
  516. [].
  517. filter_options_warning(Opt, Logger) ->
  518. log(warning,
  519. "Transport option ~p unknown or invalid.~n",
  520. [Opt], Logger).
  521. merge_options({Key, _} = Option, OptionList) ->
  522. lists:keystore(Key, 1, OptionList, Option);
  523. merge_options(Option, OptionList) ->
  524. [Option|OptionList].
  525. -spec set_option_default(Opts, atom(), any())
  526. -> Opts when Opts :: [{atom(), any()}].
  527. set_option_default(Opts, Key, Value) ->
  528. case lists:keymember(Key, 1, Opts) of
  529. true -> Opts;
  530. false -> [{Key, Value}|Opts]
  531. end.
  532. -spec require([atom()]) -> ok.
  533. require([]) ->
  534. ok;
  535. require([App|Tail]) ->
  536. case application:start(App) of
  537. ok -> ok;
  538. {error, {already_started, App}} -> ok
  539. end,
  540. require(Tail).
  541. -spec log(logger:level(), io:format(), list(), module() | #{logger => module()}) -> ok.
  542. log(Level, Format, Args, Logger) when is_atom(Logger) ->
  543. log(Level, Format, Args, #{logger => Logger});
  544. log(Level, Format, Args, #{logger := Logger})
  545. when Logger =/= error_logger ->
  546. _ = Logger:Level(Format, Args),
  547. ok;
  548. %% Because error_logger does not have all the levels
  549. %% we accept we have to do some mapping to error_logger functions.
  550. log(Level, Format, Args, _) ->
  551. Function = case Level of
  552. emergency -> error_msg;
  553. alert -> error_msg;
  554. critical -> error_msg;
  555. error -> error_msg;
  556. warning -> warning_msg;
  557. notice -> warning_msg;
  558. info -> info_msg;
  559. debug -> info_msg
  560. end,
  561. error_logger:Function(Format, Args).