diff --git a/src/esockd.erl b/src/esockd.erl index f2c0f09..ee42d82 100644 --- a/src/esockd.erl +++ b/src/esockd.erl @@ -21,18 +21,26 @@ -export([start/0]). %% Core API --export([ open/4 - , open_udp/4 - , open_dtls/4 +-export([ open/3 + , open_udp/3 + , open_dtls/3 , close/2 , close/1 + %% Legacy API + , open/4 + , open_udp/4 + , open_dtls/4 ]). -export([ reopen/1 , reopen/2 ]). --export([ child_spec/4 +-export([ child_spec/3 + , udp_child_spec/3 + , dtls_child_spec/3 + %% Legacy API + , child_spec/4 , udp_child_spec/4 , dtls_child_spec/4 ]). @@ -65,7 +73,9 @@ %% Utility functions -export([ merge_opts/2 + , changed_opts/2 , parse_opt/1 + , start_mfargs/3 , ulimit/0 , fixaddr/1 , to_string/1 @@ -94,6 +104,7 @@ -type(option() :: {acceptors, pos_integer()} | {max_connections, pos_integer()} | {max_conn_rate, conn_limit()} + | {connection_mfargs, mfargs()} | {access_rules, [esockd_access:rule()]} | {shutdown, brutal_kill | infinity | pos_integer()} | tune_buffer | {tune_buffer, boolean()} @@ -106,8 +117,10 @@ -type(host() :: inet:ip_address() | string()). -type(listen_on() :: inet:port_number() | {host(), inet:port_number()}). --type ssl_options() :: [{handshake_timeout, pos_integer()} | ssl_option()]. --type dtls_options() :: [{handshake_timeout, pos_integer()} | ssl_option()]. +-type ssl_options() :: [ssl_custom_option() | ssl_option()]. +-type dtls_options() :: [ssl_custom_option() | ssl_option()]. +-type ssl_custom_option() :: {handshake_timeout, pos_integer()} + | {gc_after_handshake, boolean()}. -type listener_ref() :: {proto(), listen_on()}. %%-------------------------------------------------------------------- @@ -123,35 +136,51 @@ start() -> %% Open & Close %% @doc Open a TCP or SSL listener --spec(open(atom(), listen_on(), [option()], mfargs()) -> {ok, pid()} | {error, term()}). -open(Proto, Port, Opts, MFA) when is_atom(Proto), is_integer(Port) -> - esockd_sup:start_listener(Proto, Port, Opts, MFA); -open(Proto, {Host, Port}, Opts, MFA) when is_atom(Proto), is_integer(Port) -> +-spec open(atom(), listen_on(), options()) -> {ok, pid()} | {error, term()}. +open(Proto, Port, Opts) when is_atom(Proto), is_integer(Port) -> + esockd_sup:start_child(child_spec(Proto, Port, Opts)); +open(Proto, {Host, Port}, Opts) when is_atom(Proto), is_integer(Port) -> {IPAddr, _Port} = fixaddr({Host, Port}), case proplists:get_value(ip, tcp_options(Opts)) of undefined -> ok; IPAddr -> ok; Other -> error({badmatch, Other}) end, - esockd_sup:start_listener(Proto, {IPAddr, Port}, Opts, MFA). + esockd_sup:start_child(child_spec(Proto, {IPAddr, Port}, Opts)). %% @private tcp_options(Opts) -> proplists:get_value(tcp_options, Opts, []). +%% @doc Open a TCP or SSL listener +-spec open(atom(), listen_on(), [option()], mfargs()) -> {ok, pid()} | {error, term()}. +open(Proto, Port, Opts, MFA) -> + open(Proto, Port, merge_mfargs(Opts, MFA)). + %% @doc Open a UDP listener --spec(open_udp(atom(), listen_on(), [option()], mfargs()) - -> {ok, pid()} - | {error, term()}). +-spec open_udp(atom(), listen_on(), [option()]) + -> {ok, pid()} | {error, term()}. +open_udp(Proto, Port, Opts) -> + esockd_sup:start_child(udp_child_spec(Proto, Port, Opts)). + +%% @doc Open a UDP listener +-spec open_udp(atom(), listen_on(), [option()], mfargs()) + -> {ok, pid()} | {error, term()}. open_udp(Proto, Port, Opts, MFA) -> - esockd_sup:start_child(udp_child_spec(Proto, Port, Opts, MFA)). + open_udp(Proto, Port, merge_mfargs(Opts, MFA)). + +%% @doc Open a DTLS listener +-spec open_dtls(atom(), listen_on(), options()) + -> {ok, pid()} | {error, term()}. +open_dtls(Proto, ListenOn, Opts) -> + esockd_sup:start_child(dtls_child_spec(Proto, ListenOn, Opts)). %% @doc Open a DTLS listener -spec(open_dtls(atom(), listen_on(), options(), mfargs()) -> {ok, pid()} | {error, term()}). open_dtls(Proto, ListenOn, Opts, MFA) -> - esockd_sup:start_child(dtls_child_spec(Proto, ListenOn, Opts, MFA)). + open_dtls(Proto, ListenOn, merge_mfargs(Opts, MFA)). %% @doc Close the listener -spec(close({atom(), listen_on()}) -> ok | {error, term()}). @@ -176,22 +205,42 @@ reopen(Proto, ListenOn) when is_atom(Proto) -> %% @doc Create a Child spec for a TCP/SSL Listener. It is a convenient method %% for creating a Child spec to hang on another Application supervisor. --spec(child_spec(atom(), listen_on(), [option()], mfargs()) - -> supervisor:child_spec()). +-spec child_spec(atom(), listen_on(), options()) + -> supervisor:child_spec(). +child_spec(Proto, ListenOn, Opts) when is_atom(Proto) -> + esockd_sup:child_spec(Proto, fixaddr(ListenOn), Opts). + +-spec child_spec(atom(), listen_on(), options(), mfargs()) + -> supervisor:child_spec(). child_spec(Proto, ListenOn, Opts, MFA) when is_atom(Proto) -> - esockd_sup:child_spec(Proto, fixaddr(ListenOn), Opts, MFA). + child_spec(Proto, ListenOn, merge_mfargs(Opts, MFA)). + +%% @doc Create a Child spec for a UDP Listener. +-spec udp_child_spec(atom(), listen_on(), options()) + -> supervisor:child_spec(). +udp_child_spec(Proto, Port, Opts) -> + esockd_sup:udp_child_spec(Proto, fixaddr(Port), Opts). %% @doc Create a Child spec for a UDP Listener. --spec(udp_child_spec(atom(), listen_on(), options(), mfargs()) - -> supervisor:child_spec()). +-spec udp_child_spec(atom(), listen_on(), options(), mfargs()) + -> supervisor:child_spec(). udp_child_spec(Proto, Port, Opts, MFA) -> - esockd_sup:udp_child_spec(Proto, fixaddr(Port), Opts, MFA). + udp_child_spec(Proto, Port, merge_mfargs(Opts, MFA)). %% @doc Create a Child spec for a DTLS Listener. --spec(dtls_child_spec(atom(), listen_on(), options(), mfargs()) - -> supervisor:child_spec()). +-spec dtls_child_spec(atom(), listen_on(), options()) + -> supervisor:child_spec(). +dtls_child_spec(Proto, ListenOn, Opts) -> + esockd_sup:dtls_child_spec(Proto, fixaddr(ListenOn), Opts). + +%% @doc Create a Child spec for a DTLS Listener. +-spec dtls_child_spec(atom(), listen_on(), options(), mfargs()) + -> supervisor:child_spec(). dtls_child_spec(Proto, ListenOn, Opts, MFA) -> - esockd_sup:dtls_child_spec(Proto, fixaddr(ListenOn), Opts, MFA). + dtls_child_spec(Proto, ListenOn, merge_mfargs(Opts, MFA)). + +merge_mfargs(Opts, MFA) -> + [{connection_mfargs, MFA} | proplists:delete(connection_mfargs, Opts)]. %%-------------------------------------------------------------------- %% Get/Set APIs @@ -276,6 +325,14 @@ deny({Proto, ListenOn}, CIDR) when is_atom(Proto) -> %%-------------------------------------------------------------------- %% Utils +-spec start_mfargs(mfargs(), _Arg1, _Arg2) -> _Ret. +start_mfargs(M, A1, A2) when is_atom(M) -> + M:start_link(A1, A2); +start_mfargs({M, F}, A1, A2) when is_atom(M), is_atom(F) -> + M:F(A1, A2); +start_mfargs({M, F, Args}, A1, A2) when is_atom(M), is_atom(F), is_list(Args) -> + erlang:apply(M, F, [A1, A2 | Args]). + %% @doc Merge two options -spec(merge_opts(proplists:proplist(), proplists:proplist()) -> proplists:proplist()). @@ -300,6 +357,19 @@ merge_opt(udp_options, Opts1, Opts2) -> merge_opts(Opts1, Opts2); merge_opt(dtls_options, Opts1, Opts2) -> merge_opts(Opts1, Opts2); merge_opt(_, _Opt1, Opt2) -> Opt2. +-spec changed_opts(proplists:proplist(), proplists:proplist()) + -> proplists:proplist(). +changed_opts(Opts, OptsRef) -> + lists:filter( + fun(Opt) -> + [Name] = proplists:get_keys([Opt]), + Value = proplists:get_value(Name, [Opt]), + ValueRef = proplists:get_value(Name, OptsRef), + ValueRef =/= Value orelse ValueRef == undefined + end, + Opts + ). + %% @doc Parse option. parse_opt(Options) -> parse_opt(Options, []). diff --git a/src/esockd_connection_sup.erl b/src/esockd_connection_sup.erl index bf8137c..395f3b1 100644 --- a/src/esockd_connection_sup.erl +++ b/src/esockd_connection_sup.erl @@ -18,9 +18,9 @@ -behaviour(gen_server). --import(proplists, [get_value/3]). +-import(proplists, [get_value/3, get_value/2]). --export([start_link/2, start_supervised/2, stop/1]). +-export([start_link/1, start_supervised/1, stop/1]). -export([ start_connection/3 , count_connections/1 @@ -67,16 +67,16 @@ error_logger:error_msg("[~s] " ++ Format, [?MODULE | Args])). %% @doc Start connection supervisor. --spec(start_link([esockd:option()], esockd:mfargs()) - -> {ok, pid()} | ignore | {error, term()}). -start_link(Opts, MFA) -> - gen_server:start_link(?MODULE, [Opts, MFA], []). +-spec start_link([esockd:option()]) + -> {ok, pid()} | ignore | {error, term()}. +start_link(Opts) -> + gen_server:start_link(?MODULE, Opts, []). --spec start_supervised(esockd:listener_ref(), esockd:mfargs()) +-spec start_supervised(esockd:listener_ref()) -> {ok, pid()} | ignore | {error, term()}. -start_supervised(ListenerRef, MFA) -> +start_supervised(ListenerRef) -> Opts = esockd_server:get_listener_prop(ListenerRef, options), - case start_link(Opts, MFA) of + case start_link(Opts) of {ok, Pid} -> _ = esockd_server:set_listener_prop(ListenerRef, connection_sup, Pid), {ok, Pid}; @@ -113,14 +113,10 @@ start_connection(Sup, Sock, UpgradeFuns) -> end. %% @doc Start the connection process. --spec(start_connection_proc(esockd:mfargs(), esockd_transport:socket()) - -> {ok, pid()} | ignore | {error, term()}). -start_connection_proc(M, Sock) when is_atom(M) -> - M:start_link(?TRANSPORT, Sock); -start_connection_proc({M, F}, Sock) when is_atom(M), is_atom(F) -> - M:F(?TRANSPORT, Sock); -start_connection_proc({M, F, Args}, Sock) when is_atom(M), is_atom(F), is_list(Args) -> - erlang:apply(M, F, [?TRANSPORT, Sock | Args]). +-spec start_connection_proc(esockd:mfargs(), esockd_transport:socket()) + -> {ok, pid()} | ignore | {error, term()}. +start_connection_proc(MFA, Sock) -> + esockd:start_mfargs(MFA, ?TRANSPORT, Sock). -spec(count_connections(pid()) -> integer()). count_connections(Sup) -> @@ -150,12 +146,13 @@ call(Sup, Req) -> %% gen_server callbacks %%-------------------------------------------------------------------- -init([Opts, MFA]) -> +init(Opts) -> process_flag(trap_exit, true), Shutdown = get_value(shutdown, Opts, brutal_kill), MaxConns = get_value(max_connections, Opts, ?DEFAULT_MAX_CONNS), RawRules = get_value(access_rules, Opts, [{allow, all}]), AccessRules = [esockd_access:compile(Rule) || Rule <- RawRules], + MFA = get_value(connection_mfargs, Opts), {ok, #state{curr_connections = #{}, max_connections = MaxConns, access_rules = AccessRules, @@ -218,7 +215,8 @@ handle_call(get_options, _From, State) -> Options = [ {shutdown, get_state_option(shutdown, State)}, {max_connections, get_state_option(max_connections, State)}, - {access_rules, get_state_option(access_rules, State)} + {access_rules, get_state_option(access_rules, State)}, + {connection_mfargs, get_state_option(connection_mfargs, State)} ], {reply, Options, State}; @@ -280,7 +278,9 @@ get_state_option(max_connections, #state{max_connections = MaxConnections}) -> get_state_option(shutdown, #state{shutdown = Shutdown}) -> Shutdown; get_state_option(access_rules, #state{access_rules = Rules}) -> - [raw(Rule) || Rule <- Rules]. + [raw(Rule) || Rule <- Rules]; +get_state_option(connection_mfargs, #state{mfargs = MFA}) -> + MFA. set_state_option({max_connections, MaxConns}, State) -> State#state{max_connections = MaxConns}; @@ -293,6 +293,8 @@ set_state_option({access_rules, Rules}, State) -> catch error:_Reason -> {error, bad_access_rules} end; +set_state_option({connection_mfargs, MFA}, State) -> + State#state{mfargs = MFA}; set_state_option(_, State) -> State. diff --git a/src/esockd_dtls_listener.erl b/src/esockd_dtls_listener.erl index 2e44b50..06c0718 100644 --- a/src/esockd_dtls_listener.erl +++ b/src/esockd_dtls_listener.erl @@ -44,10 +44,11 @@ listen_on :: esockd:listen_on(), lsock :: ssl:sslsocket(), laddr :: inet:ip_address(), - lport :: inet:port_number() + lport :: inet:port_number(), + sockopts :: [ssl:tls_server_option()] }). --type option() :: {dtls_options, [gen_tcp:option()]}. +-type option() :: {dtls_options, [gen_udp:option()]}. -define(DEFAULT_DTLS_OPTIONS, [{protocol, dtls}, @@ -95,16 +96,16 @@ init({Proto, ListenOn, Opts}) -> Port = port(ListenOn), process_flag(trap_exit, true), esockd_server:ensure_stats({Proto, ListenOn}), - SockOpts = merge_addr(ListenOn, dltsopts(Opts)), + SockOpts = merge_defaults(merge_addr(ListenOn, dltsopts(Opts))), %% Don't active the socket... - case ssl:listen(Port, esockd:merge_opts(?DEFAULT_DTLS_OPTIONS, SockOpts)) of + case ssl:listen(Port, SockOpts) of %%case ssl:listen(Port, [{active, false} | proplists:delete(active, SockOpts)]) of {ok, LSock} -> {ok, {LAddr, LPort}} = ssl:sockname(LSock), %%error_logger:info_msg("~s listen on ~s:~p with ~p acceptors.~n", %% [Proto, inet:ntoa(LAddr), LPort, AcceptorNum]), - {ok, #state{proto = Proto, listen_on = ListenOn, - lsock = LSock, laddr = LAddr, lport = LPort}}; + {ok, #state{proto = Proto, listen_on = ListenOn, lsock = LSock, + laddr = LAddr, lport = LPort, sockopts = SockOpts}}; {error, Reason} -> error_logger:error_msg("~s failed to listen on ~p - ~p (~s)", [Proto, Port, Reason, inet:format_error(Reason)]), @@ -112,14 +113,22 @@ init({Proto, ListenOn, Opts}) -> end. dltsopts(Opts) -> - proplists:delete( - handshake_timeout, - proplists:get_value(dtls_options, Opts, []) - ). + %% Filter out `esockd:ssl_custom_option()`, otherwise DTLS listener will + %% fail to start. + DTLSOpts = lists:foldl( + fun proplists:delete/2, + proplists:get_value(dtls_options, Opts, []), + [handshake_timeout, gc_after_handshake] + ), + SockOpts = proplists:get_value(udp_options, Opts, []), + SockOpts ++ DTLSOpts. port(Port) when is_integer(Port) -> Port; port({_Addr, Port}) -> Port. +merge_defaults(SockOpts) -> + esockd:merge_opts(?DEFAULT_DTLS_OPTIONS, SockOpts). + merge_addr(Port, SockOpts) when is_integer(Port) -> SockOpts; merge_addr({Addr, _Port}, SockOpts) -> @@ -134,10 +143,13 @@ handle_call(get_state, _From, State = #state{lsock = LSock, lport = LPort}) -> ], {reply, Reply, State}; -handle_call({set_options, Opts}, _From, State = #state{lsock = LSock}) -> - case ssl:setopts(LSock, dltsopts(Opts)) of +handle_call({set_options, Opts}, _From, State = #state{lsock = LSock, sockopts = SockOpts}) -> + SockOptsIn = dltsopts(Opts), + SockOptsChanged = esockd:changed_opts(SockOptsIn, SockOpts), + case ssl:setopts(LSock, SockOptsChanged) of ok -> - {reply, ok, State}; + SockOptsMerged = esockd:merge_opts(SockOpts, SockOptsChanged), + {reply, ok, State#state{sockopts = SockOptsMerged}}; Error = {error, _} -> %% Setting dTLS options on listening socket always succeeds, %% even if the options are invalid. diff --git a/src/esockd_listener.erl b/src/esockd_listener.erl index eda34d0..ed775ed 100644 --- a/src/esockd_listener.erl +++ b/src/esockd_listener.erl @@ -44,7 +44,8 @@ listen_on :: esockd:listen_on(), lsock :: inet:socket(), laddr :: inet:ip_address(), - lport :: inet:port_number() + lport :: inet:port_number(), + sockopts :: [gen_tcp:listen_option()] }). -type option() :: {tcp_options, [gen_tcp:option()]}. @@ -97,14 +98,14 @@ init({Proto, ListenOn, Opts}) -> Port = port(ListenOn), process_flag(trap_exit, true), esockd_server:ensure_stats({Proto, ListenOn}), - SockOpts = merge_addr(ListenOn, sockopts(Opts)), - case esockd_transport:listen(Port, esockd:merge_opts(?DEFAULT_TCP_OPTIONS, SockOpts)) of + SockOpts = merge_addr(ListenOn, merge_defaults(sockopts(Opts))), + case esockd_transport:listen(Port, SockOpts) of {ok, LSock} -> {ok, {LAddr, LPort}} = inet:sockname(LSock), %%error_logger:info_msg("~s listen on ~s:~p with ~p acceptors.~n", %% [Proto, inet:ntoa(LAddr), LPort, AcceptorNum]), - {ok, #state{proto = Proto, listen_on = ListenOn, - lsock = LSock, laddr = LAddr, lport = LPort}}; + {ok, #state{proto = Proto, listen_on = ListenOn, lsock = LSock, + laddr = LAddr, lport = LPort, sockopts = SockOpts}}; {error, Reason} -> error_logger:error_msg("~s failed to listen on ~p - ~p (~s)", [Proto, Port, Reason, inet:format_error(Reason)]), @@ -116,6 +117,9 @@ sockopts(Opts) -> SockOpts = proplists:get_value(tcp_options, Opts, []), [{active, false} | proplists:delete(active, SockOpts)]. +merge_defaults(SockOpts) -> + esockd:merge_opts(?DEFAULT_TCP_OPTIONS, SockOpts). + port(Port) when is_integer(Port) -> Port; port({_Addr, Port}) -> Port. @@ -136,10 +140,13 @@ handle_call(get_state, _From, State = #state{lsock = LSock, lport = LPort}) -> ], {reply, Reply, State}; -handle_call({set_options, Opts}, _From, State = #state{lsock = LSock}) -> - case inet:setopts(LSock, sockopts(Opts)) of +handle_call({set_options, Opts}, _From, State = #state{lsock = LSock, sockopts = SockOpts}) -> + SockOptsIn = sockopts(Opts), + SockOptsChanged = esockd:changed_opts(SockOptsIn, SockOpts), + case inet:setopts(LSock, SockOptsChanged) of ok -> - {reply, ok, State}; + SockOptsMerged = esockd:merge_opts(SockOpts, SockOptsChanged), + {reply, ok, State#state{sockopts = SockOptsMerged}}; Error = {error, _} -> {reply, Error, State} end; diff --git a/src/esockd_listener_sup.erl b/src/esockd_listener_sup.erl index 46e9dba..efd3cad 100644 --- a/src/esockd_listener_sup.erl +++ b/src/esockd_listener_sup.erl @@ -20,8 +20,7 @@ -include("esockd.hrl"). --export([ start_link/5 - , start_link/3 +-export([ start_link/2 , listener/1 , acceptor_sup/1 , connection_sup/1 @@ -56,30 +55,16 @@ , start_acceptors/1 ]). --type listen_type() :: tcp | dtls. - %%-------------------------------------------------------------------- %% APIs %%-------------------------------------------------------------------- %% @doc Start listener supervisor --spec start_link(listen_type(), atom(), esockd:listen_on(), [esockd:option()], esockd:mfargs()) - -> {ok, pid()} | {error, term()}. -start_link(Type, Proto, ListenOn, Opts, MFA) -> - %% NOTE - %% `Opts` are still part of the childspec, which means if whole listener supervisor - %% restarts, any changes done through `set_options` will be lost. - ListenerRef = {Proto, ListenOn}, - _ = esockd_server:set_listener_prop(ListenerRef, type, Type), - _ = esockd_server:set_listener_prop(ListenerRef, options, Opts), - supervisor:start_link(?MODULE, {ListenerRef, MFA}). - -%% @doc Start listener supervisor --spec start_link(atom(), esockd:listen_on(), esockd:mfargs()) +-spec start_link(atom(), esockd:listen_on()) -> {ok, pid()} | {error, term()}. -start_link(Proto, ListenOn, MFA) -> +start_link(Proto, ListenOn) -> ListenerRef = {Proto, ListenOn}, - supervisor:start_link(?MODULE, {ListenerRef, MFA}). + supervisor:start_link(?MODULE, ListenerRef). %% @doc Get listener. -spec(listener(pid()) -> {module(), pid()}). @@ -125,7 +110,7 @@ set_options(ListenerRef, Sup, Opts) -> %% Restore previous options _ = esockd_server:set_listener_prop(ListenerRef, options, OptsWas), ok = esockd_connection_sup:set_options(ConnSup, OptsWas), - ok = Listener:set_options(ListenerPid, OptsWas), + %% Listener has failed to set options, no need to restore Error end, ok = restart_acceptor_sup(ListenerRef, Sup), @@ -181,9 +166,9 @@ deny(Sup, CIDR) -> %% Supervisor callbacks %%-------------------------------------------------------------------- -init({ListenerRef, MFA}) -> +init(ListenerRef) -> ConnSup = #{id => connection_sup, - start => {esockd_connection_sup, start_supervised, [ListenerRef, MFA]}, + start => {esockd_connection_sup, start_supervised, [ListenerRef]}, restart => transient, shutdown => infinity, type => supervisor, diff --git a/src/esockd_sup.erl b/src/esockd_sup.erl index 29291f2..5565a58 100644 --- a/src/esockd_sup.erl +++ b/src/esockd_sup.erl @@ -22,7 +22,7 @@ -export([child_id/2]). --export([ start_listener/4 +-export([ start_child/1 , stop_listener/2 , restart_listener/2 ]). @@ -32,10 +32,9 @@ , listener_and_module/1 ]). --export([ child_spec/4 - , udp_child_spec/4 - , dtls_child_spec/4 - , start_child/1 +-export([ child_spec/3 + , udp_child_spec/3 + , dtls_child_spec/3 ]). %% supervisor callback @@ -49,42 +48,37 @@ start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). --spec(start_listener(atom(), esockd:listen_on(), [esockd:option()], esockd:mfargs()) - -> {ok, pid()} | {error, term()}). -start_listener(Proto, ListenOn, Opts, MFA) -> - start_child(child_spec(Proto, ListenOn, Opts, MFA)). - --spec(child_spec(atom(), esockd:listen_on(), [esockd:option()], esockd:mfargs()) - -> supervisor:child_spec()). -child_spec(Proto, ListenOn, Opts, MFA) when is_atom(Proto) -> +-spec child_spec(atom(), esockd:listen_on(), [esockd:option()]) + -> supervisor:child_spec(). +child_spec(Proto, ListenOn, Opts) when is_atom(Proto) -> ListenerRef = {Proto, ListenOn}, _ = esockd_server:set_listener_prop(ListenerRef, type, tcp), _ = esockd_server:set_listener_prop(ListenerRef, options, Opts), #{id => child_id(Proto, ListenOn), - start => {esockd_listener_sup, start_link, [Proto, ListenOn, MFA]}, + start => {esockd_listener_sup, start_link, [Proto, ListenOn]}, restart => transient, shutdown => infinity, type => supervisor, modules => [esockd_listener_sup]}. --spec(udp_child_spec(atom(), esockd:listen_on(), [esockd:option()], esockd:mfargs()) - -> supervisor:child_spec()). -udp_child_spec(Proto, Port, Opts, MFA) when is_atom(Proto) -> +-spec udp_child_spec(atom(), esockd:listen_on(), [esockd:option()]) + -> supervisor:child_spec(). +udp_child_spec(Proto, Port, Opts) when is_atom(Proto) -> #{id => child_id(Proto, Port), - start => {esockd_udp, server, [Proto, Port, Opts, MFA]}, + start => {esockd_udp, server, [Proto, Port, Opts]}, restart => transient, shutdown => 5000, type => worker, modules => [esockd_udp]}. --spec(dtls_child_spec(atom(), esockd:listen_on(), [esockd:option()], esockd:mfargs()) - -> supervisor:child_spec()). -dtls_child_spec(Proto, Port, Opts, MFA) when is_atom(Proto) -> +-spec dtls_child_spec(atom(), esockd:listen_on(), [esockd:option()]) + -> supervisor:child_spec(). +dtls_child_spec(Proto, Port, Opts) when is_atom(Proto) -> ListenerRef = {Proto, Port}, _ = esockd_server:set_listener_prop(ListenerRef, type, dtls), _ = esockd_server:set_listener_prop(ListenerRef, options, Opts), #{id => child_id(Proto, Port), - start => {esockd_listener_sup, start_link, [Proto, Port, MFA]}, + start => {esockd_listener_sup, start_link, [Proto, Port]}, restart => transient, shutdown => infinity, type => supervisor, diff --git a/src/esockd_udp.erl b/src/esockd_udp.erl index 7f75e27..465510c 100644 --- a/src/esockd_udp.erl +++ b/src/esockd_udp.erl @@ -20,7 +20,7 @@ -import(esockd_listener_sup, [conn_rate_limiter/1, conn_limiter_opts/2, conn_limiter_opt/2]). --export([ server/4 +-export([ server/3 , count_peers/1 , stop/1 ]). @@ -79,10 +79,10 @@ %% API %%-------------------------------------------------------------------- --spec(server(atom(), esockd:listen_on(), [gen_udp:option()], mfa()) +-spec(server(atom(), esockd:listen_on(), [esockd:option()]) -> {ok, pid()} | {error, term()}). -server(Proto, ListenOn, Opts, MFA) -> - gen_server:start_link(?MODULE, [Proto, ListenOn, Opts, MFA], []). +server(Proto, ListenOn, Opts) -> + gen_server:start_link(?MODULE, [Proto, ListenOn, Opts], []). resolve_addr(Port, SockOpts) when is_integer(Port) -> SockOpts; @@ -153,10 +153,11 @@ deny(Pid, CIDR) -> %% gen_server callbacks %%-------------------------------------------------------------------- -init([Proto, ListenOn, Opts, MFA]) -> +init([Proto, ListenOn, Opts]) -> process_flag(trap_exit, true), put(incoming_peers, 0), + MFA = proplists:get_value(connection_mfargs, Opts), RawRules = proplists:get_value(access_rules, Opts, [{allow, all}]), AccessRules = [esockd_access:compile(Rule) || Rule <- RawRules], @@ -346,8 +347,8 @@ should_throttle(#state{max_peers = infinity}) -> false; should_throttle(#state{max_peers = MaxLimit, peers = Peers}) -> (maps:size(Peers) div 2) > MaxLimit. -start_channel(Transport, Peer, #state{mfa = {M, F, Args}}) -> - erlang:apply(M, F, [Transport, Peer | Args]). +start_channel(Transport, Peer, #state{mfa = MFA}) -> + esockd:start_mfargs(MFA, Transport, Peer). activate_sock(State = #state{sock = Sock}) -> ok = inet:setopts(Sock, [{active, ?ACTIVE_N}]), State. diff --git a/test/const_server.erl b/test/const_server.erl new file mode 100644 index 0000000..6e666d8 --- /dev/null +++ b/test/const_server.erl @@ -0,0 +1,45 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2020 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(const_server). + +-export([start_link/3]). + +%% Callbacks +-export([init/3, loop/3]). + +start_link(Transport, RawSock, Resp) -> + {ok, spawn_link(?MODULE, init, [Transport, RawSock, Resp])}. + +init(Transport, RawSock, Resp) -> + case Transport:wait(RawSock) of + {ok, Sock} -> + loop(Transport, Sock, Resp); + {error, Reason} -> + {error, Reason} + end. + +loop(Transport, Sock, Resp) -> + case Transport:recv(Sock, 0) of + {ok, _Data} -> + Transport:send(Sock, Resp), + loop(Transport, Sock, Resp); + {shutdown, Reason} -> + exit({shutdown, Reason}); + {error, Reason} -> + exit({shutdown, Reason}) + end. + diff --git a/test/esockd_SUITE.erl b/test/esockd_SUITE.erl index edb7f1c..62e474d 100644 --- a/test/esockd_SUITE.erl +++ b/test/esockd_SUITE.erl @@ -33,16 +33,16 @@ end_per_suite(_Config) -> application:stop(esockd). t_open_close(_) -> - {ok, _LSup} = esockd:open(echo, {"127.0.0.1", 3000}, [binary, {packet, raw}], - {echo_server, start_link, []}), + {ok, _LSup} = esockd:open(echo, {"127.0.0.1", 3000}, + [binary, {packet, raw}, {connection_mfargs, echo_server}]), {ok, Sock} = gen_tcp:connect("127.0.0.1", 3000, [binary, {active, false}]), ok = gen_tcp:send(Sock, <<"Hello">>), {ok, <<"Hello">>} = gen_tcp:recv(Sock, 0), ok = esockd:close(echo, {"127.0.0.1", 3000}). t_reopen(_) -> - {ok, _LSup} = esockd:open(echo, {"127.0.0.1", 3000}, [binary, {packet, raw}], - {echo_server, start_link, []}), + {ok, _LSup} = esockd:open(echo, {"127.0.0.1", 3000}, + [binary, {packet, raw}, {connection_mfargs, echo_server}]), {ok, Sock1} = gen_tcp:connect("127.0.0.1", 3000, [{active, false}]), ok = gen_tcp:send(Sock1, <<"Hello">>), timer:sleep(10), @@ -53,8 +53,9 @@ t_reopen(_) -> ok = esockd:close(echo, {"127.0.0.1", 3000}). t_reopen_1(_) -> - {ok, _LSup} = esockd:open(echo, 7000, [{max_connections, 4}, {acceptors, 4}], - {echo_server, start_link, []}), + {ok, _LSup} = esockd:open(echo, 7000, + [{max_connections, 4}, {acceptors, 4}, + {connection_mfargs, echo_server}]), timer:sleep(10), ok = esockd:reopen({echo, 7000}), ?assertEqual(4, esockd:get_max_connections({echo, 7000})), @@ -63,7 +64,8 @@ t_reopen_1(_) -> t_reopen_fail(_) -> LPort = 4001, - {ok, _LSup} = esockd:open(echo, LPort, [{acceptors, 4}], {echo_server, start_link, []}), + {ok, _LSup} = esockd:open(echo, LPort, + [{acceptors, 4}, {connection_mfargs, {echo_server, start_link}}]), {error, not_found} = esockd:reopen({echo, 5000}), ?assertEqual(4, esockd:get_acceptors({echo, LPort})), {ok, Sock} = gen_tcp:connect({127,0,0,1}, LPort, [binary, {active, false}]), @@ -72,14 +74,17 @@ t_reopen_fail(_) -> ok = esockd:close(echo, LPort). t_open_udp(_) -> - {ok, _} = esockd:open_udp(echo, 5678, [], {udp_echo_server, start_link, []}), + {ok, _} = esockd:open_udp(echo, 5678, + [{connection_mfargs, {udp_echo_server, start_link}}]), {ok, Sock} = gen_udp:open(0, [binary, {active, false}]), ok = gen_udp:send(Sock, {127,0,0,1}, 5678, <<"Hi">>), {ok, {_Addr, 5678, <<"Hi">>}} = gen_udp:recv(Sock, 0), ok = esockd:close(echo, 5678). t_udp_child_spec(_) -> - Spec = esockd:udp_child_spec(echo, 5000, [], {udp_echo_server, start_link, []}), + MFA = {udp_echo_server, start_link, []}, + Spec = esockd:udp_child_spec(echo, 5000, [{connection_mfargs, MFA}]), + Spec = esockd:udp_child_spec(echo, 5000, [], MFA), #{id := {listener_sup,{echo,5000}}, modules := [esockd_udp], restart := transient, @@ -88,14 +93,13 @@ t_udp_child_spec(_) -> } = Spec. t_open_dtls(Config) -> - DtlsOpts = [{mode, binary}, - {reuseaddr, true}, - {certfile, esockd_ct:certfile(Config)}, + DtlsOpts = [{certfile, esockd_ct:certfile(Config)}, {keyfile, esockd_ct:keyfile(Config)}, {verify, verify_none} ], - {ok, _} = esockd:open_dtls(echo, 5000, [{dtls_options, DtlsOpts}], - {dtls_echo_server, start_link, []}), + {ok, _} = esockd:open_dtls(echo, 5000, + [{dtls_options, DtlsOpts}, + {connection_mfargs, dtls_echo_server}]), {ok, Sock} = ssl:connect({127,0,0,1}, 5000, [binary, {protocol, dtls}, {active, false}, @@ -107,7 +111,9 @@ t_open_dtls(Config) -> ok = esockd:close(echo, 5000). t_dtls_child_spec(_) -> - Spec = esockd:dtls_child_spec(echo, 8883, [], {udp_echo_server, start_link, []}), + MFA = {udp_echo_server, start_link, []}, + Spec = esockd:dtls_child_spec(echo, 8883, [{connection_mfargs, MFA}]), + Spec = esockd:dtls_child_spec(echo, 8883, [], MFA), #{id := {listener_sup,{echo,8883}}, modules := [esockd_listener_sup], restart := transient, @@ -116,7 +122,9 @@ t_dtls_child_spec(_) -> } = Spec. t_child_spec(_) -> - Spec = esockd:child_spec(echo, 5000, [], {echo_server, start_link, []}), + MFA = {udp_echo_server, start_link, []}, + Spec = esockd:child_spec(echo, 5000, [{connection_mfargs, MFA}]), + Spec = esockd:child_spec(echo, 5000, [], MFA), #{id := {listener_sup, {echo,5000}}, modules := [esockd_listener_sup], restart := transient, @@ -125,7 +133,7 @@ t_child_spec(_) -> } = Spec. t_listeners(_) -> - {ok, LSup} = esockd:open(echo, 6000, [], {echo_server, start_link, []}), + {ok, LSup} = esockd:open(echo, 6000, [{connection_mfargs, echo_server}]), [{{echo, 6000}, LSup}] = esockd:listeners(), ?assertEqual(LSup, esockd:listener({echo, 6000})), ok = esockd:close(echo, 6000), @@ -133,7 +141,7 @@ t_listeners(_) -> ?assertException(error, not_found, esockd:listener({echo, 6000})). t_get_stats(_) -> - {ok, _LSup} = esockd:open(echo, 6000, [], {echo_server, start_link, []}), + {ok, _LSup} = esockd:open(echo, 6000, [{connection_mfargs, echo_server}]), {ok, Sock1} = gen_tcp:connect("127.0.0.1", 6000, [{active, false}]), {ok, Sock2} = gen_tcp:connect("127.0.0.1", 6000, [{active, false}]), timer:sleep(10), @@ -145,44 +153,50 @@ t_get_stats(_) -> ok = esockd:close(echo, 6000). t_get_options(_) -> - {ok, _LSup} = esockd:open(echo, 6000, [{acceptors, 4}], - {echo_server, start_link, []}), - [{acceptors, 4}] = esockd:get_options({echo, 6000}), + {ok, _LSup} = esockd:open(echo, 6000, + [{acceptors, 4}, + {connection_mfargs, {echo_server, start_link}}]), + [{acceptors, 4}, + {connection_mfargs, {echo_server, start_link}}] = esockd:get_options({echo, 6000}), ok = esockd:close(echo, 6000), ?assertException(error, not_found, esockd:get_options({echo, 6000})), - {ok, _LSup1} = esockd:open_dtls(dtls_echo, 6000, [{acceptors, 4}], - {dtls_echo_server, start_link, []}), - [{acceptors, 4}] = esockd:get_options({dtls_echo, 6000}), + {ok, _LSup1} = esockd:open_dtls(dtls_echo, 6000, + [{acceptors, 4}, + {connection_mfargs, {dtls_echo_server, start_link}}]), + [{acceptors, 4}, + {connection_mfargs, {dtls_echo_server, start_link}}] = esockd:get_options({dtls_echo, 6000}), ok = esockd:close(dtls_echo, 6000), ?assertException(error, not_found, esockd:get_options({dtls_echo, 6000})), - {ok, _LSup2} = esockd:open_udp(udp_echo, 6000, [{acceptors, 4}], - {udp_echo_server, start_link, []}), - [{acceptors, 4}] = esockd:get_options({udp_echo, 6000}), + {ok, _LSup2} = esockd:open_udp(udp_echo, 6000, + [{acceptors, 4}, + {connection_mfargs, {udp_echo_server, start_link, []}}]), + [{acceptors, 4}, + {connection_mfargs, {udp_echo_server, start_link, []}}] = esockd:get_options({udp_echo, 6000}), ok = esockd:close(udp_echo, 6000), ?assertException(error, not_found, esockd:get_options({udp_echo, 6000})). t_get_acceptors(_) -> - {ok, _LSup} = esockd:open(echo, 6000, [{acceptors, 4}], - {echo_server, start_link, []}), + {ok, _LSup} = esockd:open(echo, 6000, + [{acceptors, 4}, {connection_mfargs, echo_server}]), ?assertEqual(4, esockd:get_acceptors({echo, 6000})), ok = esockd:close(echo, 6000), - {ok, _LSup1} = esockd:open_dtls(dtls_echo, 6000, [{acceptors, 4}], - {dtls_echo_server, start_link, []}), + {ok, _LSup1} = esockd:open_dtls(dtls_echo, 6000, + [{acceptors, 4}, {connection_mfargs, dtls_echo_server}]), ?assertEqual(4, esockd:get_acceptors({dtls_echo, 6000})), ok = esockd:close(dtls_echo, 6000), - {ok, _LSup2} = esockd:open_udp(udp_echo, 6000, [{acceptors, 4}], - {udp_echo_server, start_link, []}), + {ok, _LSup2} = esockd:open_udp(udp_echo, 6000, + [{acceptors, 4}, {connection_mfargs, udp_echo_server}]), %% fixed 1 ?assertEqual(1, esockd:get_acceptors({udp_echo, 6000})), ok = esockd:close(udp_echo, 6000). t_get_set_max_connections(_) -> - {ok, _LSup} = esockd:open(echo, 7000, [{max_connections, 4}], - {echo_server, start_link, []}), + {ok, _LSup} = esockd:open(echo, 7000, + [{max_connections, 4}, {connection_mfargs, echo_server}]), ?assertEqual(4, esockd:get_max_connections({echo, 7000})), {ok, _Sock1} = gen_tcp:connect("localhost", 7000, [{active, false}]), {ok, _Sock2} = gen_tcp:connect("localhost", 7000, [{active, false}]), @@ -192,15 +206,17 @@ t_get_set_max_connections(_) -> ?assertEqual({error, closed}, gen_tcp:recv(Sock3, 0)), ok = esockd:close(echo, 7000), - {ok, _LSup1} = esockd:open_dtls(dtls_echo, 7000, [{max_connections, 4}], - {dtls_echo_server, start_link, []}), + {ok, _LSup1} = esockd:open_dtls(dtls_echo, 7000, + [{max_connections, 4}, + {connection_mfargs, dtls_echo_server}]), ?assertEqual(4, esockd:get_max_connections({dtls_echo, 7000})), esockd:set_max_connections({dtls_echo, 7000}, 16), ?assertEqual(16, esockd:get_max_connections({dtls_echo, 7000})), ok = esockd:close(dtls_echo, 7000), - {ok, _LSup2} = esockd:open_udp(udp_echo, 7000, [{max_connections, 4}], - {udp_echo_server, start_link, []}), + {ok, _LSup2} = esockd:open_udp(udp_echo, 7000, + [{max_connections, 4}, + {connection_mfargs, udp_echo_server}]), ?assertEqual(4, esockd:get_max_connections({udp_echo, 7000})), esockd:set_max_connections({udp_echo, 7000}, 16), ?assertEqual(16, esockd:get_max_connections({udp_echo, 7000})), @@ -208,8 +224,8 @@ t_get_set_max_connections(_) -> t_get_set_max_conn_rate(_) -> LimiterOpt = #{module => esockd_limiter, capacity => 100, interval => 1}, - {ok, _LSup} = esockd:open(echo, 7000, [{limiter, LimiterOpt}], - {echo_server, start_link, []}), + {ok, _LSup} = esockd:open(echo, 7000, + [{limiter, LimiterOpt}, {connection_mfargs, echo_server}]), ?assertEqual({100, 1}, esockd:get_max_conn_rate({echo, 7000})), esockd:set_max_conn_rate({echo, 7000}, LimiterOpt#{capacity := 50, interval := 2}), ?assertEqual({50, 2}, esockd:get_max_conn_rate({echo, 7000})), @@ -217,16 +233,18 @@ t_get_set_max_conn_rate(_) -> ?assertException(error, not_found, esockd:get_max_conn_rate({echo, 7000})), - {ok, _LSup1} = esockd:open_dtls(dtls_echo, 7000, [{limiter, LimiterOpt}], - {dtls_echo_server, start_link, []}), + {ok, _LSup1} = esockd:open_dtls(dtls_echo, 7000, + [{limiter, LimiterOpt}, + {connection_mfargs, dtls_echo_server}]), ?assertEqual({100, 1}, esockd:get_max_conn_rate({dtls_echo, 7000})), esockd:set_max_conn_rate({dtls_echo, 7000}, LimiterOpt#{capacity := 50, interval := 2}), ?assertEqual({50, 2}, esockd:get_max_conn_rate({dtls_echo, 7000})), ok = esockd:close(dtls_echo, 7000), ?assertException(error, not_found, esockd:get_max_conn_rate({dtls_echo, 7000})), - {ok, _LSup2} = esockd:open_udp(udp_echo, 7000, [{limiter, LimiterOpt}], - {udp_echo_server, start_link, []}), + {ok, _LSup2} = esockd:open_udp(udp_echo, 7000, + [{limiter, LimiterOpt}, + {connection_mfargs, udp_echo_server}]), ?assertEqual({100, 1}, esockd:get_max_conn_rate({udp_echo, 7000})), esockd:set_max_conn_rate({udp_echo, 7000}, LimiterOpt#{capacity := 50, interval := 2}), ?assertEqual({50, 2}, esockd:get_max_conn_rate({udp_echo, 7000})), @@ -234,7 +252,7 @@ t_get_set_max_conn_rate(_) -> ?assertException(error, not_found, esockd:get_max_conn_rate({udp_echo, 7000})). t_get_current_connections(Config) -> - {ok, _LSup} = esockd:open(echo, 7000, [], {echo_server, start_link, []}), + {ok, _LSup} = esockd:open(echo, 7000, [{connection_mfargs, {echo_server, start_link, []}}]), {ok, Sock1} = gen_tcp:connect("127.0.0.1", 7000, [{active, false}]), {ok, Sock2} = gen_tcp:connect("127.0.0.1", 7000, [{active, false}]), timer:sleep(10), @@ -245,14 +263,15 @@ t_get_current_connections(Config) -> ?assertEqual(0, esockd:get_current_connections({echo, 7000})), ok = esockd:close(echo, 7000), - DtlsOpts = [{mode, binary}, - {reuseaddr, true}, - {certfile, esockd_ct:certfile(Config)}, + UdpOpts = [{mode, binary}, {reuseaddr, true}], + DtlsOpts = [{certfile, esockd_ct:certfile(Config)}, {keyfile, esockd_ct:keyfile(Config)}, {verify, verify_none} ], ClientOpts = [binary, {protocol, dtls}, {verify, verify_none}], - {ok, _LSup1} = esockd:open_dtls(dtls_echo, 7000, [{dtls_options, DtlsOpts}], {dtls_echo_server, start_link, []}), + {ok, _LSup1} = esockd:open_dtls(dtls_echo, 7000, + [{dtls_options, DtlsOpts}, {udp_options, UdpOpts}, + {connection_mfargs, dtls_echo_server}]), {ok, DtlsSock1} = ssl:connect({127,0,0,1}, 7000, ClientOpts, 5000), {ok, DtlsSock2} = ssl:connect({127,0,0,1}, 7000, ClientOpts, 5000), timer:sleep(10), @@ -261,7 +280,7 @@ t_get_current_connections(Config) -> ok = ssl:close(DtlsSock2), ok = esockd:close(dtls_echo, 7000), - {ok, _LSup2} = esockd:open_udp(udp_echo, 7001, [], {udp_echo_server, start_link, []}), + {ok, _LSup2} = esockd:open_udp(udp_echo, 7001, [{connection_mfargs, udp_echo_server}]), {ok, UdpSock1} = gen_udp:open(0, [binary, {active, false}]), {ok, UdpSock2} = gen_udp:open(0, [binary, {active, false}]), gen_udp:send(UdpSock1, {127,0,0,1}, 7001, <<"test">>), @@ -273,7 +292,7 @@ t_get_current_connections(Config) -> ok = esockd:close(udp_echo, 7001). t_get_shutdown_count(Config) -> - {ok, _LSup} = esockd:open(echo, 7000, [], {echo_server, start_link, []}), + {ok, _LSup} = esockd:open(echo, 7000, [{connection_mfargs, echo_server}]), {ok, Sock1} = gen_tcp:connect("127.0.0.1", 7000, [{active, false}]), {ok, Sock2} = gen_tcp:connect("127.0.0.1", 7000, [{active, false}]), ok = gen_tcp:close(Sock1), @@ -282,14 +301,15 @@ t_get_shutdown_count(Config) -> ?assertEqual([{closed, 2}], esockd:get_shutdown_count({echo, 7000})), ok = esockd:close(echo, 7000), - DtlsOpts = [{mode, binary}, - {reuseaddr, true}, - {certfile, esockd_ct:certfile(Config)}, + UdpOpts = [{mode, binary}, {reuseaddr, true}], + DtlsOpts = [{certfile, esockd_ct:certfile(Config)}, {keyfile, esockd_ct:keyfile(Config)}, {verify, verify_none} ], ClientOpts = [binary, {protocol, dtls}, {verify, verify_none}], - {ok, _LSup1} = esockd:open_dtls(dtls_echo, 7000, [{dtls_options, DtlsOpts}], {dtls_echo_server, start_link, []}), + {ok, _LSup1} = esockd:open_dtls(dtls_echo, 7000, + [{dtls_options, DtlsOpts}, {udp_options, UdpOpts}, + {connection_mfargs, dtls_echo_server}]), {ok, DtlsSock1} = ssl:connect({127,0,0,1}, 7000, ClientOpts, 5000), {ok, DtlsSock2} = ssl:connect({127,0,0,1}, 7000, ClientOpts, 5000), ok = ssl:close(DtlsSock1), @@ -298,7 +318,7 @@ t_get_shutdown_count(Config) -> ?assertEqual([], esockd:get_shutdown_count({dtls_echo, 7000})), ok = esockd:close(dtls_echo, 7000), - {ok, _LSup2} = esockd:open_udp(udp_echo, 7001, [], {udp_echo_server, start_link, []}), + {ok, _LSup2} = esockd:open_udp(udp_echo, 7001, [{connection_mfargs, udp_echo_server}]), {ok, UdpSock1} = gen_udp:open(0, [binary, {active, false}]), {ok, UdpSock2} = gen_udp:open(0, [binary, {active, false}]), gen_udp:send(UdpSock1, {127,0,0,1}, 7001, <<"test">>), @@ -310,22 +330,42 @@ t_get_shutdown_count(Config) -> ok = esockd:close(udp_echo, 7001). t_update_options(_) -> - {ok, _LSup} = esockd:open(echo, 6000, [{acceptors, 4}], - {echo_server, start_link, []}), + {ok, _LSup} = esockd:open(echo, 6000, + [{acceptors, 4}, + {tcp_options, [{backlog, 128}]}, + {connection_mfargs, echo_server}]), ?assertEqual(4, esockd:get_acceptors({echo, 6000})), {ok, Sock1} = gen_tcp:connect("127.0.0.1", 6000, [binary, {active, false}]), - ok = esockd:set_options({echo, 6000}, [{acceptors, 16}, tune_buffer]), + %% Backlog size can not be changed + ?assertEqual( + {error, einval}, + esockd:set_options({echo, 6000}, + [{acceptors, 8}, + {tcp_options, [{backlog, 256}]}]) + ), + %% Number of acceptors haven't changed + ?assertEqual(4, esockd:get_acceptors({echo, 6000})), + %% Other TCP options are changeable + ?assertEqual( + ok, + esockd:set_options({echo, 6000}, + [{acceptors, 16}, + tune_buffer, + {tcp_options, [{send_timeout_close, false}]}, + {connection_mfargs, {const_server, start_link, [<<"HEY">>]}}]) + ), {ok, Sock2} = gen_tcp:connect("127.0.0.1", 6000, [binary, {active, false}]), ?assertEqual(16, esockd:get_acceptors({echo, 6000})), + %% Sockets should still be alive ok = gen_tcp:send(Sock1, <<"Sock1">>), {ok, <<"Sock1">>} = gen_tcp:recv(Sock1, 0), ok = gen_tcp:send(Sock2, <<"Sock2">>), - {ok, <<"Sock2">>} = gen_tcp:recv(Sock2, 0), + {ok, <<"HEY">>} = gen_tcp:recv(Sock2, 0), ok = esockd:close(echo, 6000). t_update_options_error(_) -> - {ok, _LSup} = esockd:open(echo, 6000, [{acceptors, 4}], - {echo_server, start_link, []}), + {ok, _LSup} = esockd:open(echo, 6000, + [{acceptors, 4}, {connection_mfargs, echo_server}]), ?assertEqual(4, esockd:get_acceptors({echo, 6000})), {ok, Sock1} = gen_tcp:connect("127.0.0.1", 6000, [binary, {active, false}]), ?assertEqual( {error, bad_access_rules} @@ -360,8 +400,8 @@ t_update_tls_options(Config) -> , {cacertfile, esockd_ct:cacertfile(Config)} , {customize_hostname_check, [{match_fun, fun(_, _) -> true end}]} ], - {ok, _LSup} = esockd:open(echo_tls, LPort, [{ssl_options, SslOpts1}], - {echo_server, start_link, []}), + {ok, _LSup} = esockd:open(echo_tls, LPort, + [{ssl_options, SslOpts1}, {connection_mfargs, echo_server}]), {ok, Sock1} = ssl:connect("localhost", LPort, ClientSslOpts, 1000), ok = esockd:set_options({echo_tls, LPort}, [{ssl_options, [{verify, verify_peer}]}]), @@ -386,8 +426,7 @@ t_update_tls_options(Config) -> t_allow_deny(_) -> AccessRules = [{allow, "192.168.1.0/24"}], - {ok, _LSup} = esockd:open(echo, 7000, [{access_rules, AccessRules}], - {echo_server, start_link, []}), + {ok, _LSup} = esockd:open(echo, 7000, [{access_rules, AccessRules}]), ?assertEqual([{allow, "192.168.1.0/24"}], esockd:get_access_rules({echo, 7000})), ok = esockd:allow({echo, 7000}, "10.10.0.0/16"), ?assertEqual([{allow, "10.10.0.0/16"}, @@ -402,8 +441,7 @@ t_allow_deny(_) -> %% dtls - {ok, _LSup1} = esockd:open_dtls(dtls_echo, 7000, [{access_rules, AccessRules}], - {dtls_echo_server, start_link, []}), + {ok, _LSup1} = esockd:open_dtls(dtls_echo, 7000, [{access_rules, AccessRules}]), ?assertEqual([{allow, "192.168.1.0/24"}], esockd:get_access_rules({dtls_echo, 7000})), ok = esockd:allow({dtls_echo, 7000}, "10.10.0.0/16"), ?assertEqual([{allow, "10.10.0.0/16"}, @@ -418,8 +456,7 @@ t_allow_deny(_) -> %% udp - {ok, _LSup2} = esockd:open_dtls(udp_echo, 7001, [{access_rules, AccessRules}], - {udp_echo_server, start_link, []}), + {ok, _LSup2} = esockd:open_dtls(udp_echo, 7001, [{access_rules, AccessRules}]), ?assertEqual([{allow, "192.168.1.0/24"}], esockd:get_access_rules({udp_echo, 7001})), ok = esockd:allow({udp_echo, 7001}, "10.10.0.0/16"), ?assertEqual([{allow, "10.10.0.0/16"}, @@ -447,6 +484,16 @@ t_merge_opts(_) -> ], ?assertEqual(Result, esockd:merge_opts(Opts1, Opts2)). +t_changed_opts(_) -> + Opts1 = [ binary, {acceptors, 8}, {tune_buffer, true} + , {ssl_options, [{keyfile, "key.pem"}, {certfile, "cert.pem"}]} + ], + Opts2 = [ binary, inet6, {acceptors, 16} + , {ssl_options, [{keyfile, "key.pem"}, {certfile, "cert.pem"}]} + ], + Result = [inet6, {acceptors, 16}], + ?assertEqual(Result, esockd:changed_opts(Opts2, Opts1)). + t_parse_opt(_) -> Opts = [{acceptors, 10}, {tune_buffer, true}, {proxy_protocol, true}, {ssl_options, []}], ?assertEqual(Opts, esockd:parse_opt([{badopt1, val1}, {badopt2, val2}|Opts])). @@ -470,8 +517,9 @@ t_tune_fun_overload(_) -> Ret = {error, overloaded}, LPort = 7003, Name = tune_echo_overload, - {ok, _LSup} = esockd:open(Name, LPort, [{tune_fun, {?MODULE, sock_tune_fun, [Ret]}}], - {echo_server, start_link, []}), + {ok, _LSup} = esockd:open(Name, LPort, + [{tune_fun, {?MODULE, sock_tune_fun, [Ret]}}, + {connection_mfargs, {echo_server, start_link, []}}]), {ok, Socket} = gen_tcp:connect("127.0.0.1", LPort, [{active, true}]), receive {tcp_closed, S} -> @@ -493,8 +541,8 @@ t_tune_fun_ok(_) -> LPort = 7004, Name = tune_echo_ok, {ok, _LSup} = esockd:open(Name, LPort, - [{tune_fun, {?MODULE, sock_tune_fun, [Ret]}}], - {echo_server, start_link, []}), + [{tune_fun, {?MODULE, sock_tune_fun, [Ret]}}, + {connection_mfargs, echo_server}]), {ok, _S} = gen_tcp:connect("127.0.0.1", LPort, [{active, true}]), timer:sleep(10), Cnts = esockd_server:get_stats({Name, LPort}), @@ -524,8 +572,7 @@ do_listener_handle_port_exit(Config, IsTls) -> false -> [] end, %% GIVEN: when listener is started - {ok, LSup} = esockd:open(Name, LPort, OpenOpts, - {echo_server, start_link, []}), + {ok, LSup} = esockd:open(Name, LPort, [{connection_mfargs, echo_server} | OpenOpts]), {LMod, LPid} = esockd_listener_sup:listener(LSup), LState = LMod:get_state(LPid), PortInUse = proplists:get_value(listen_port, LState), diff --git a/test/esockd_connection_sup_SUITE.erl b/test/esockd_connection_sup_SUITE.erl index fd4365f..3d9ce91 100644 --- a/test/esockd_connection_sup_SUITE.erl +++ b/test/esockd_connection_sup_SUITE.erl @@ -114,7 +114,7 @@ t_handle_unexpected(_) -> {noreply, state} = esockd_connection_sup:handle_info(info, state). with_conn_sup(Opts, Fun) -> - {ok, ConnSup} = esockd_connection_sup:start_link(Opts, {echo_server, start_link, []}), + {ok, ConnSup} = esockd_connection_sup:start_link([{connection_mfargs, echo_server} | Opts]), Fun(ConnSup), ok = esockd_connection_sup:stop(ConnSup). diff --git a/test/esockd_udp_SUITE.erl b/test/esockd_udp_SUITE.erl index 5b7d97f..d21e885 100644 --- a/test/esockd_udp_SUITE.erl +++ b/test/esockd_udp_SUITE.erl @@ -68,8 +68,11 @@ udp_send_and_recv(Sock, Port, Data) -> ok. with_udp_server(TestFun) -> - MFA = {?MODULE, udp_echo_init, []}, - {ok, Srv} = esockd_udp:server(test, {{127,0,0,1}, 6000}, [], MFA), + dbg:tracer(), + dbg:p(all, c), + dbg:tpl({emqx_connection_sup, '_', '_'}, x), + MFA = {?MODULE, udp_echo_init}, + {ok, Srv} = esockd_udp:server(test, {{127,0,0,1}, 6000}, [{connection_mfargs, MFA}]), TestFun(Srv, 6000), ok = esockd_udp:stop(Srv).