Skip to content

Commit

Permalink
feat: support get/set listener opts
Browse files Browse the repository at this point in the history
  • Loading branch information
qzhuyan committed Dec 9, 2024
1 parent 6e7d0a7 commit 13f7b58
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 29 deletions.
14 changes: 1 addition & 13 deletions src/quicer_conn_acceptor_sup.erl
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@ start_link(ListenerH, ConnOpts) ->
-spec init(Args :: term()) ->
{ok, {SupFlags :: supervisor:sup_flags(), [ChildSpec :: supervisor:child_spec()]}}
| ignore.
init([ListenerH, Opts]) ->
OptsTab = init_opts_tab(Opts),
init([ListenerH, OptsTab]) ->
SupFlags = #{
strategy => simple_one_for_one,
intensity => 1,
Expand All @@ -81,14 +80,3 @@ init([ListenerH, Opts]) ->
%%%===================================================================
%%% Internal functions
%%%===================================================================
init_opts_tab({LOpts, COpts, SOpts}) ->
SharedTab = ets:new(quicer_listener_tab, [set, {keypos, 1}, {read_concurrency, true}]),
true = store_config(SharedTab, l_opts, LOpts),
true = store_config(SharedTab, c_opts, COpts),
true = store_config(SharedTab, s_opts, SOpts),
SharedTab.

store_config(Tab, K, V) when is_list(V) ->
store_config(Tab, K, maps:from_list(V));
store_config(Tab, K, V) ->
true = ets:insert(Tab, {K, V}).
51 changes: 42 additions & 9 deletions src/quicer_listener.erl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
unlock/2,
reload/2,
reload/3,
get_conf/2,
get_handle/2,
count_conns/1
]).
Expand All @@ -45,7 +46,7 @@
listener :: quicer:listener_handle(),
conn_sup :: pid(),
alpn :: [string()],
opts :: quicer:listener_opts()
opts_tab :: ets:tid()
}).

-export_type([listener_name/0]).
Expand Down Expand Up @@ -113,6 +114,11 @@ get_handle(Pid, Timeout) ->
count_conns(Pid) ->
gen_server:call(Pid, count_conns, infinity).

%% @doc get the listener configuration
-spec get_conf(pid(), timeout()) -> {map(), map(), map()}.
get_conf(Pid, Timeout) ->
gen_server:call(Pid, get_conf, Timeout).

%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
Expand All @@ -134,15 +140,16 @@ init([Name, ListenOn, {LOpts, COpts, SOpts}]) when is_list(LOpts) ->
init([Name, ListenOn, {maps:from_list(LOpts), COpts, SOpts}]);
init([Name, ListenOn, {#{conn_acceptors := N, alpn := Alpn} = LOpts, _COpts, _SOpts} = Opts]) ->
process_flag(trap_exit, true),
OptsTab = init_opts_tab(Opts),
{ok, L} = quicer:listen(ListenOn, maps:without([conn_acceptors], LOpts)),
{ok, ConnSup} = supervisor:start_link(quicer_conn_acceptor_sup, [L, Opts]),
{ok, ConnSup} = supervisor:start_link(quicer_conn_acceptor_sup, [L, OptsTab]),
_ = [{ok, _} = supervisor:start_child(ConnSup, [ConnSup]) || _ <- lists:seq(1, N)],
{ok, #state{
name = Name,
listen_on = ListenOn,
listener = L,
conn_sup = ConnSup,
opts = LOpts,
opts_tab = OptsTab,
alpn = Alpn
}}.

Expand All @@ -167,10 +174,11 @@ handle_call(lock, _From, State) ->
Res = quicer:stop_listener(State#state.listener),
{reply, Res, State};
handle_call(unlock, _From, State) ->
LOpts = ets:lookup_element(State#state.opts_tab, l_opts, 2),
Res = quicer:start_listener(
State#state.listener,
State#state.listen_on,
State#state.opts
LOpts
),
{reply, Res, State};
handle_call({reload, NewConf}, _From, State) ->
Expand All @@ -184,12 +192,21 @@ handle_call(
_From,
#state{
conn_sup = ConnSup,
opts = #{conn_acceptors := NoAcceptors}
opts_tab = OptsTab
} =
State
) ->
ConnPids = supervisor:which_children(ConnSup),
{reply, length(ConnPids) - NoAcceptors, State};
#{conn_acceptors := NoAcceptors} = ets:lookup_element(OptsTab, l_opts, 2),
{active, ActiveCnt} = lists:nth(2, supervisor:count_children(ConnSup)),
{reply, ActiveCnt - NoAcceptors, State};
handle_call(get_conf, _From, #state{opts_tab = OptsTab} = State) ->
{reply,
{
ets:lookup_element(OptsTab, l_opts, 2),
ets:lookup_element(OptsTab, c_opts, 2),
ets:lookup_element(OptsTab, s_opts, 2)
},
State};
handle_call(Request, _From, State) ->
Reply = {error, {unimpl, Request}},
{reply, Reply, State}.
Expand Down Expand Up @@ -245,7 +262,7 @@ terminate(_Reason, #state{listener = L}) ->
ok.

-spec do_reload(quicer:listen_on(), map(), #state{}) -> {ok | {error, any()}, #state{}}.
do_reload(ListenOn, NewConf, State) ->
do_reload(ListenOn, NewConf, #state{opts_tab = OptsTab} = State) ->
_ = quicer:stop_listener(State#state.listener),
Res = quicer:start_listener(
State#state.listener,
Expand All @@ -254,7 +271,23 @@ do_reload(ListenOn, NewConf, State) ->
),
case Res of
ok ->
{ok, State#state{listen_on = ListenOn, opts = NewConf}};
true = ets:insert(OptsTab, {l_opts, to_map(NewConf)}),
{ok, State#state{listen_on = ListenOn}};
Error ->
{Error, State}
end.

init_opts_tab({LOpts, COpts, SOpts}) ->
Tab = ets:new(quicer_listener_tab, [set, {keypos, 1}, {read_concurrency, true}]),
%% @NOTE: Be careful with the lifecyle of the table.
true = ets:insert(Tab, [
{l_opts, to_map(LOpts)},
{c_opts, to_map(COpts)},
{s_opts, to_map(SOpts)}
]),
Tab.

to_map(Opts) when is_list(Opts) ->
maps:from_list(Opts);
to_map(Opts) when is_map(Opts) ->
Opts.
20 changes: 13 additions & 7 deletions test/quicer_listener_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -521,17 +521,23 @@ tc_listener_conf_reload(Config) ->
%% WHEN: the listener is reloaded with new listener opts (New cert, key and cacert).
ok = quicer_listener:lock(QuicApp, infinity),
ok = quicer_listener:unlock(QuicApp, infinity),
NewListenerOpts =
ListenerOpts ++
[
{certfile, filename:join(DataDir, "other-server.pem")},
{keyfile, filename:join(DataDir, "other-server.key")},
{cacertfile, filename:join(DataDir, "other-ca.pem")}
],

NewCerts = [
{certfile, filename:join(DataDir, "other-server.pem")},
{keyfile, filename:join(DataDir, "other-server.key")},
{cacertfile, filename:join(DataDir, "other-ca.pem")}
],
NewListenerOpts = ListenerOpts ++ NewCerts,
ok = quicer_listener:reload(QuicApp, NewListenerOpts),
%% THEN: the listener handle is unchanged
?assertEqual({ok, LHandle}, quicer_listener:get_handle(QuicApp, 5000)),

%% THEN: the listener conf is changed
{LMap, CMap, SMap} = quicer_listener:get_conf(QuicApp, 5000),
?assertEqual(LMap, maps:from_list(NewListenerOpts)),
?assertEqual(CMap, maps:from_list(ConnectionOpts)),
?assertEqual(SMap, maps:from_list(StreamOpts)),

%% THEN: start new connection with old cacert must fail
?assertMatch(
{error, transport_down, #{error := _, status := Status}} when
Expand Down

0 comments on commit 13f7b58

Please sign in to comment.