Skip to content

Commit 55a2566

Browse files
committed
feat(opts): turn connection process MFA into option
Thus, making it possible to change it at runtime with the help of `esockd:setopts/2`.
1 parent abf1524 commit 55a2566

File tree

9 files changed

+268
-166
lines changed

9 files changed

+268
-166
lines changed

src/esockd.erl

Lines changed: 77 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,26 @@
2121
-export([start/0]).
2222

2323
%% Core API
24-
-export([ open/4
25-
, open_udp/4
26-
, open_dtls/4
24+
-export([ open/3
25+
, open_udp/3
26+
, open_dtls/3
2727
, close/2
2828
, close/1
29+
%% Legacy API
30+
, open/4
31+
, open_udp/4
32+
, open_dtls/4
2933
]).
3034

3135
-export([ reopen/1
3236
, reopen/2
3337
]).
3438

35-
-export([ child_spec/4
39+
-export([ child_spec/3
40+
, udp_child_spec/3
41+
, dtls_child_spec/3
42+
%% Legacy API
43+
, child_spec/4
3644
, udp_child_spec/4
3745
, dtls_child_spec/4
3846
]).
@@ -67,6 +75,7 @@
6775
-export([ merge_opts/2
6876
, changed_opts/2
6977
, parse_opt/1
78+
, start_mfargs/3
7079
, ulimit/0
7180
, fixaddr/1
7281
, to_string/1
@@ -95,6 +104,7 @@
95104
-type(option() :: {acceptors, pos_integer()}
96105
| {max_connections, pos_integer()}
97106
| {max_conn_rate, conn_limit()}
107+
| {connection_mfargs, mfargs()}
98108
| {access_rules, [esockd_access:rule()]}
99109
| {shutdown, brutal_kill | infinity | pos_integer()}
100110
| tune_buffer | {tune_buffer, boolean()}
@@ -126,35 +136,51 @@ start() ->
126136
%% Open & Close
127137

128138
%% @doc Open a TCP or SSL listener
129-
-spec(open(atom(), listen_on(), [option()], mfargs()) -> {ok, pid()} | {error, term()}).
130-
open(Proto, Port, Opts, MFA) when is_atom(Proto), is_integer(Port) ->
131-
esockd_sup:start_listener(Proto, Port, Opts, MFA);
132-
open(Proto, {Host, Port}, Opts, MFA) when is_atom(Proto), is_integer(Port) ->
139+
-spec open(atom(), listen_on(), options()) -> {ok, pid()} | {error, term()}.
140+
open(Proto, Port, Opts) when is_atom(Proto), is_integer(Port) ->
141+
esockd_sup:start_child(child_spec(Proto, Port, Opts));
142+
open(Proto, {Host, Port}, Opts) when is_atom(Proto), is_integer(Port) ->
133143
{IPAddr, _Port} = fixaddr({Host, Port}),
134144
case proplists:get_value(ip, tcp_options(Opts)) of
135145
undefined -> ok;
136146
IPAddr -> ok;
137147
Other -> error({badmatch, Other})
138148
end,
139-
esockd_sup:start_listener(Proto, {IPAddr, Port}, Opts, MFA).
149+
esockd_sup:start_child(child_spec(Proto, {IPAddr, Port}, Opts)).
140150

141151
%% @private
142152
tcp_options(Opts) ->
143153
proplists:get_value(tcp_options, Opts, []).
144154

155+
%% @doc Open a TCP or SSL listener
156+
-spec open(atom(), listen_on(), [option()], mfargs()) -> {ok, pid()} | {error, term()}.
157+
open(Proto, Port, Opts, MFA) ->
158+
open(Proto, Port, merge_mfargs(Opts, MFA)).
159+
145160
%% @doc Open a UDP listener
146-
-spec(open_udp(atom(), listen_on(), [option()], mfargs())
147-
-> {ok, pid()}
148-
| {error, term()}).
161+
-spec open_udp(atom(), listen_on(), [option()])
162+
-> {ok, pid()} | {error, term()}.
163+
open_udp(Proto, Port, Opts) ->
164+
esockd_sup:start_child(udp_child_spec(Proto, Port, Opts)).
165+
166+
%% @doc Open a UDP listener
167+
-spec open_udp(atom(), listen_on(), [option()], mfargs())
168+
-> {ok, pid()} | {error, term()}.
149169
open_udp(Proto, Port, Opts, MFA) ->
150-
esockd_sup:start_child(udp_child_spec(Proto, Port, Opts, MFA)).
170+
open_udp(Proto, Port, merge_mfargs(Opts, MFA)).
171+
172+
%% @doc Open a DTLS listener
173+
-spec open_dtls(atom(), listen_on(), options())
174+
-> {ok, pid()} | {error, term()}.
175+
open_dtls(Proto, ListenOn, Opts) ->
176+
esockd_sup:start_child(dtls_child_spec(Proto, ListenOn, Opts)).
151177

152178
%% @doc Open a DTLS listener
153179
-spec(open_dtls(atom(), listen_on(), options(), mfargs())
154180
-> {ok, pid()}
155181
| {error, term()}).
156182
open_dtls(Proto, ListenOn, Opts, MFA) ->
157-
esockd_sup:start_child(dtls_child_spec(Proto, ListenOn, Opts, MFA)).
183+
open_dtls(Proto, ListenOn, merge_mfargs(Opts, MFA)).
158184

159185
%% @doc Close the listener
160186
-spec(close({atom(), listen_on()}) -> ok | {error, term()}).
@@ -179,22 +205,42 @@ reopen(Proto, ListenOn) when is_atom(Proto) ->
179205

180206
%% @doc Create a Child spec for a TCP/SSL Listener. It is a convenient method
181207
%% for creating a Child spec to hang on another Application supervisor.
182-
-spec(child_spec(atom(), listen_on(), [option()], mfargs())
183-
-> supervisor:child_spec()).
208+
-spec child_spec(atom(), listen_on(), options())
209+
-> supervisor:child_spec().
210+
child_spec(Proto, ListenOn, Opts) when is_atom(Proto) ->
211+
esockd_sup:child_spec(Proto, fixaddr(ListenOn), Opts).
212+
213+
-spec child_spec(atom(), listen_on(), options(), mfargs())
214+
-> supervisor:child_spec().
184215
child_spec(Proto, ListenOn, Opts, MFA) when is_atom(Proto) ->
185-
esockd_sup:child_spec(Proto, fixaddr(ListenOn), Opts, MFA).
216+
child_spec(Proto, ListenOn, merge_mfargs(Opts, MFA)).
217+
218+
%% @doc Create a Child spec for a UDP Listener.
219+
-spec udp_child_spec(atom(), listen_on(), options())
220+
-> supervisor:child_spec().
221+
udp_child_spec(Proto, Port, Opts) ->
222+
esockd_sup:udp_child_spec(Proto, fixaddr(Port), Opts).
186223

187224
%% @doc Create a Child spec for a UDP Listener.
188-
-spec(udp_child_spec(atom(), listen_on(), options(), mfargs())
189-
-> supervisor:child_spec()).
225+
-spec udp_child_spec(atom(), listen_on(), options(), mfargs())
226+
-> supervisor:child_spec().
190227
udp_child_spec(Proto, Port, Opts, MFA) ->
191-
esockd_sup:udp_child_spec(Proto, fixaddr(Port), Opts, MFA).
228+
udp_child_spec(Proto, Port, merge_mfargs(Opts, MFA)).
192229

193230
%% @doc Create a Child spec for a DTLS Listener.
194-
-spec(dtls_child_spec(atom(), listen_on(), options(), mfargs())
195-
-> supervisor:child_spec()).
231+
-spec dtls_child_spec(atom(), listen_on(), options())
232+
-> supervisor:child_spec().
233+
dtls_child_spec(Proto, ListenOn, Opts) ->
234+
esockd_sup:dtls_child_spec(Proto, fixaddr(ListenOn), Opts).
235+
236+
%% @doc Create a Child spec for a DTLS Listener.
237+
-spec dtls_child_spec(atom(), listen_on(), options(), mfargs())
238+
-> supervisor:child_spec().
196239
dtls_child_spec(Proto, ListenOn, Opts, MFA) ->
197-
esockd_sup:dtls_child_spec(Proto, fixaddr(ListenOn), Opts, MFA).
240+
dtls_child_spec(Proto, ListenOn, merge_mfargs(Opts, MFA)).
241+
242+
merge_mfargs(Opts, MFA) ->
243+
[{connection_mfargs, MFA} | proplists:delete(connection_mfargs, Opts)].
198244

199245
%%--------------------------------------------------------------------
200246
%% Get/Set APIs
@@ -279,6 +325,14 @@ deny({Proto, ListenOn}, CIDR) when is_atom(Proto) ->
279325
%%--------------------------------------------------------------------
280326
%% Utils
281327

328+
-spec start_mfargs(mfargs(), _Arg1, _Arg2) -> _Ret.
329+
start_mfargs(M, A1, A2) when is_atom(M) ->
330+
M:start_link(A1, A2);
331+
start_mfargs({M, F}, A1, A2) when is_atom(M), is_atom(F) ->
332+
M:F(A1, A2);
333+
start_mfargs({M, F, Args}, A1, A2) when is_atom(M), is_atom(F), is_list(Args) ->
334+
erlang:apply(M, F, [A1, A2 | Args]).
335+
282336
%% @doc Merge two options
283337
-spec(merge_opts(proplists:proplist(), proplists:proplist())
284338
-> proplists:proplist()).

src/esockd_connection_sup.erl

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818

1919
-behaviour(gen_server).
2020

21-
-import(proplists, [get_value/3]).
21+
-import(proplists, [get_value/3, get_value/2]).
2222

23-
-export([start_link/2, start_supervised/2, stop/1]).
23+
-export([start_link/1, start_supervised/1, stop/1]).
2424

2525
-export([ start_connection/3
2626
, count_connections/1
@@ -67,16 +67,16 @@
6767
error_logger:error_msg("[~s] " ++ Format, [?MODULE | Args])).
6868

6969
%% @doc Start connection supervisor.
70-
-spec(start_link([esockd:option()], esockd:mfargs())
71-
-> {ok, pid()} | ignore | {error, term()}).
72-
start_link(Opts, MFA) ->
73-
gen_server:start_link(?MODULE, [Opts, MFA], []).
70+
-spec start_link([esockd:option()])
71+
-> {ok, pid()} | ignore | {error, term()}.
72+
start_link(Opts) ->
73+
gen_server:start_link(?MODULE, Opts, []).
7474

75-
-spec start_supervised(esockd:listener_ref(), esockd:mfargs())
75+
-spec start_supervised(esockd:listener_ref())
7676
-> {ok, pid()} | ignore | {error, term()}.
77-
start_supervised(ListenerRef, MFA) ->
77+
start_supervised(ListenerRef) ->
7878
Opts = esockd_server:get_listener_prop(ListenerRef, options),
79-
case start_link(Opts, MFA) of
79+
case start_link(Opts) of
8080
{ok, Pid} ->
8181
_ = esockd_server:set_listener_prop(ListenerRef, connection_sup, Pid),
8282
{ok, Pid};
@@ -113,14 +113,10 @@ start_connection(Sup, Sock, UpgradeFuns) ->
113113
end.
114114

115115
%% @doc Start the connection process.
116-
-spec(start_connection_proc(esockd:mfargs(), esockd_transport:socket())
117-
-> {ok, pid()} | ignore | {error, term()}).
118-
start_connection_proc(M, Sock) when is_atom(M) ->
119-
M:start_link(?TRANSPORT, Sock);
120-
start_connection_proc({M, F}, Sock) when is_atom(M), is_atom(F) ->
121-
M:F(?TRANSPORT, Sock);
122-
start_connection_proc({M, F, Args}, Sock) when is_atom(M), is_atom(F), is_list(Args) ->
123-
erlang:apply(M, F, [?TRANSPORT, Sock | Args]).
116+
-spec start_connection_proc(esockd:mfargs(), esockd_transport:socket())
117+
-> {ok, pid()} | ignore | {error, term()}.
118+
start_connection_proc(MFA, Sock) ->
119+
esockd:start_mfargs(MFA, ?TRANSPORT, Sock).
124120

125121
-spec(count_connections(pid()) -> integer()).
126122
count_connections(Sup) ->
@@ -150,12 +146,13 @@ call(Sup, Req) ->
150146
%% gen_server callbacks
151147
%%--------------------------------------------------------------------
152148

153-
init([Opts, MFA]) ->
149+
init(Opts) ->
154150
process_flag(trap_exit, true),
155151
Shutdown = get_value(shutdown, Opts, brutal_kill),
156152
MaxConns = get_value(max_connections, Opts, ?DEFAULT_MAX_CONNS),
157153
RawRules = get_value(access_rules, Opts, [{allow, all}]),
158154
AccessRules = [esockd_access:compile(Rule) || Rule <- RawRules],
155+
MFA = get_value(connection_mfargs, Opts),
159156
{ok, #state{curr_connections = #{},
160157
max_connections = MaxConns,
161158
access_rules = AccessRules,
@@ -218,7 +215,8 @@ handle_call(get_options, _From, State) ->
218215
Options = [
219216
{shutdown, get_state_option(shutdown, State)},
220217
{max_connections, get_state_option(max_connections, State)},
221-
{access_rules, get_state_option(access_rules, State)}
218+
{access_rules, get_state_option(access_rules, State)},
219+
{connection_mfargs, get_state_option(connection_mfargs, State)}
222220
],
223221
{reply, Options, State};
224222

@@ -280,7 +278,9 @@ get_state_option(max_connections, #state{max_connections = MaxConnections}) ->
280278
get_state_option(shutdown, #state{shutdown = Shutdown}) ->
281279
Shutdown;
282280
get_state_option(access_rules, #state{access_rules = Rules}) ->
283-
[raw(Rule) || Rule <- Rules].
281+
[raw(Rule) || Rule <- Rules];
282+
get_state_option(connection_mfargs, #state{mfargs = MFA}) ->
283+
MFA.
284284

285285
set_state_option({max_connections, MaxConns}, State) ->
286286
State#state{max_connections = MaxConns};
@@ -293,6 +293,8 @@ set_state_option({access_rules, Rules}, State) ->
293293
catch
294294
error:_Reason -> {error, bad_access_rules}
295295
end;
296+
set_state_option({connection_mfargs, MFA}, State) ->
297+
State#state{mfargs = MFA};
296298
set_state_option(_, State) ->
297299
State.
298300

src/esockd_listener_sup.erl

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@
2020

2121
-include("esockd.hrl").
2222

23-
-export([ start_link/5
24-
, start_link/3
23+
-export([ start_link/2
2524
, listener/1
2625
, acceptor_sup/1
2726
, connection_sup/1
@@ -56,30 +55,16 @@
5655
, start_acceptors/1
5756
]).
5857

59-
-type listen_type() :: tcp | dtls.
60-
6158
%%--------------------------------------------------------------------
6259
%% APIs
6360
%%--------------------------------------------------------------------
6461

6562
%% @doc Start listener supervisor
66-
-spec start_link(listen_type(), atom(), esockd:listen_on(), [esockd:option()], esockd:mfargs())
67-
-> {ok, pid()} | {error, term()}.
68-
start_link(Type, Proto, ListenOn, Opts, MFA) ->
69-
%% NOTE
70-
%% `Opts` are still part of the childspec, which means if whole listener supervisor
71-
%% restarts, any changes done through `set_options` will be lost.
72-
ListenerRef = {Proto, ListenOn},
73-
_ = esockd_server:set_listener_prop(ListenerRef, type, Type),
74-
_ = esockd_server:set_listener_prop(ListenerRef, options, Opts),
75-
supervisor:start_link(?MODULE, {ListenerRef, MFA}).
76-
77-
%% @doc Start listener supervisor
78-
-spec start_link(atom(), esockd:listen_on(), esockd:mfargs())
63+
-spec start_link(atom(), esockd:listen_on())
7964
-> {ok, pid()} | {error, term()}.
80-
start_link(Proto, ListenOn, MFA) ->
65+
start_link(Proto, ListenOn) ->
8166
ListenerRef = {Proto, ListenOn},
82-
supervisor:start_link(?MODULE, {ListenerRef, MFA}).
67+
supervisor:start_link(?MODULE, ListenerRef).
8368

8469
%% @doc Get listener.
8570
-spec(listener(pid()) -> {module(), pid()}).
@@ -181,9 +166,9 @@ deny(Sup, CIDR) ->
181166
%% Supervisor callbacks
182167
%%--------------------------------------------------------------------
183168

184-
init({ListenerRef, MFA}) ->
169+
init(ListenerRef) ->
185170
ConnSup = #{id => connection_sup,
186-
start => {esockd_connection_sup, start_supervised, [ListenerRef, MFA]},
171+
start => {esockd_connection_sup, start_supervised, [ListenerRef]},
187172
restart => transient,
188173
shutdown => infinity,
189174
type => supervisor,

0 commit comments

Comments
 (0)