Skip to content

Commit

Permalink
feat: support restart listener process with new opts
Browse files Browse the repository at this point in the history
  • Loading branch information
qzhuyan committed Jan 3, 2024
1 parent 859472b commit 8c56a02
Show file tree
Hide file tree
Showing 8 changed files with 263 additions and 76 deletions.
107 changes: 71 additions & 36 deletions c_src/quicer_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,9 @@ ServerLoadConfiguration(ErlNifEnv *env,
}

unsigned alpn_buffer_length = 0;
QUIC_BUFFER alpn_buffers[MAX_ALPN] = { 0 };
QUIC_BUFFER *alpn_buffers = NULL;

if (!load_alpn(env, option, &alpn_buffer_length, alpn_buffers))
if (!load_alpn(env, option, &alpn_buffer_length, &alpn_buffers))
{
return ATOM_ALPN;
}
Expand All @@ -240,14 +240,15 @@ ServerLoadConfiguration(ErlNifEnv *env,
// Allocate/initialize the configuration object, with the configured ALPN
// and settings.
//
QUIC_STATUS Status = QUIC_STATUS_SUCCESS;
if (QUIC_FAILED(Status = MsQuic->ConfigurationOpen(Registration,
alpn_buffers,
alpn_buffer_length,
&Settings,
sizeof(Settings),
CredConfig, // Context
Configuration)))
QUIC_STATUS Status = MsQuic->ConfigurationOpen(Registration,
alpn_buffers,
alpn_buffer_length,
&Settings,
sizeof(Settings),
CredConfig, // Context
Configuration);
free_alpn_buffers(alpn_buffers, alpn_buffer_length);
if (QUIC_FAILED(Status))
{
return ATOM_STATUS(Status);
}
Expand Down Expand Up @@ -313,9 +314,9 @@ ClientLoadConfiguration(ErlNifEnv *env,
}

unsigned alpn_buffer_length = 0;
QUIC_BUFFER alpn_buffers[MAX_ALPN];
QUIC_BUFFER *alpn_buffers = NULL;

if (!load_alpn(env, options, &alpn_buffer_length, alpn_buffers))
if (!load_alpn(env, options, &alpn_buffer_length, &alpn_buffers))
{
ret = ATOM_ALPN;
goto done;
Expand All @@ -325,14 +326,15 @@ ClientLoadConfiguration(ErlNifEnv *env,
// Allocate/initialize the configuration object, with the configured ALPN
// and settings.
//
QUIC_STATUS Status = QUIC_STATUS_SUCCESS;
if (QUIC_FAILED(Status = MsQuic->ConfigurationOpen(Registration,
alpn_buffers,
alpn_buffer_length,
&Settings,
sizeof(Settings),
NULL,
Configuration)))
QUIC_STATUS Status = MsQuic->ConfigurationOpen(Registration,
alpn_buffers,
alpn_buffer_length,
&Settings,
sizeof(Settings),
NULL,
Configuration);
free_alpn_buffers(alpn_buffers, alpn_buffer_length);
if (QUIC_FAILED(Status))
{
ret = ATOM_STATUS(Status);
goto done;
Expand All @@ -357,50 +359,83 @@ ClientLoadConfiguration(ErlNifEnv *env,
return ret;
}

/*
** load alpn from eterm options to the alpn_buffers
** @NOTE 1:caller must call free_alpn_buffers after use
*/
bool
load_alpn(ErlNifEnv *env,
const ERL_NIF_TERM *options,
unsigned *alpn_buffer_length,
QUIC_BUFFER alpn_buffers[])
QUIC_BUFFER **alpn_buffers)
{
ERL_NIF_TERM alpn_list;
assert(*alpn_buffers == NULL);
if (!enif_get_map_value(env, *options, ATOM_ALPN, &alpn_list))
{
return false;
}

if (!enif_get_list_length(env, alpn_list, alpn_buffer_length))
if (!enif_get_list_length(env, alpn_list, alpn_buffer_length)
|| alpn_buffer_length == 0)
{
return false;
}

ERL_NIF_TERM head, tail;
*alpn_buffers = malloc((*alpn_buffer_length) * sizeof(QUIC_BUFFER));

if (!enif_get_list_cell(env, alpn_list, &head, &tail))
if (!*alpn_buffers)
{
return false;
}

for (int i = 0; i < (int)(*alpn_buffer_length); i++)
CxPlatZeroMemory(*alpn_buffers, (*alpn_buffer_length) * sizeof(QUIC_BUFFER));

ERL_NIF_TERM list, head, tail;
unsigned i = 0;
list = alpn_list;
while (enif_get_list_cell(env, list, &head, &tail))
{
// @todo check if PATH_MAX is the correct length
char str[PATH_MAX];
if (enif_get_string(env, head, str, PATH_MAX, ERL_NIF_LATIN1) <= 0)
unsigned len = 0;
#if ERL_NIF_MINOR_VERSION > 16
if (!enif_get_string_length(env, head, &len, ERL_NIF_LATIN1))
#else
if (!enif_get_list_length(env, head, &len))
#endif
{
return false;
goto exit;
}
len++; // for '\0'
char *str = malloc(len * sizeof(char));

alpn_buffers[i].Buffer = (uint8_t *)str;
alpn_buffers[i].Length = strlen(str);

if (!enif_get_list_cell(env, tail, &head, &tail)
&& i + 1 < (int)(*alpn_buffer_length))
if (enif_get_string(env, head, str, len, ERL_NIF_LATIN1) <= 0)
{
return false;
free(str);
str = NULL;
goto exit;
}
}

(*alpn_buffers)[i].Buffer = (uint8_t *)str;
(*alpn_buffers)[i].Length = len - 1; // msquic doesn't need '\0'
i++;
list = tail;
}
return true;

exit:
free_alpn_buffers(*alpn_buffers, i);
return false;
}

void
free_alpn_buffers(QUIC_BUFFER *alpn_buffers, unsigned len)
{
for (unsigned i = 0; i < len; i++)
{
free(alpn_buffers[i].Buffer);
}
free(alpn_buffers);
alpn_buffers = NULL;
}

bool
Expand Down
5 changes: 4 additions & 1 deletion c_src/quicer_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ ERL_NIF_TERM ClientLoadConfiguration(ErlNifEnv *env,
bool load_alpn(ErlNifEnv *env,
const ERL_NIF_TERM *option,
unsigned *alpn_buffer_length,
QUIC_BUFFER alpn_buffers[]);
QUIC_BUFFER **alpn_buffers);

void free_alpn_buffers(QUIC_BUFFER *alpn_buffers, unsigned alpn_buffer_length);

bool load_verify(ErlNifEnv *env,
const ERL_NIF_TERM *option,
const bool default_verify);
Expand Down
47 changes: 22 additions & 25 deletions c_src/quicer_listener.c
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ listen2(ErlNifEnv *env, __unused_parm__ int argc, const ERL_NIF_TERM argv[])

// Now try to start listener
unsigned alpn_buffer_length = 0;
QUIC_BUFFER alpn_buffers[MAX_ALPN];
QUIC_BUFFER *alpn_buffers = NULL;

// Allow insecure, default is false
ERL_NIF_TERM eisInsecure;
Expand All @@ -420,16 +420,18 @@ listen2(ErlNifEnv *env, __unused_parm__ int argc, const ERL_NIF_TERM argv[])
l_ctx->allow_insecure = TRUE;
}

if (!load_alpn(env, &options, &alpn_buffer_length, alpn_buffers))
if (!load_alpn(env, &options, &alpn_buffer_length, &alpn_buffers))
{
ret = ERROR_TUPLE_2(ATOM_ALPN);
goto exit;
}

// Start Listener
if (QUIC_FAILED(
Status = MsQuic->ListenerStart(
l_ctx->Listener, alpn_buffers, alpn_buffer_length, &Address)))
Status = MsQuic->ListenerStart(
l_ctx->Listener, alpn_buffers, alpn_buffer_length, &Address);
free_alpn_buffers(alpn_buffers, alpn_buffer_length);

if (QUIC_FAILED(Status))
{
TP_NIF_3(start_fail, (uintptr_t)(l_ctx->Listener), Status);
HQUIC Listener = l_ctx->Listener;
Expand Down Expand Up @@ -525,7 +527,7 @@ start_listener3(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])

QuicerListenerCTX *l_ctx;
unsigned alpn_buffer_length = 0;
QUIC_BUFFER alpn_buffers[MAX_ALPN];
QUIC_BUFFER *alpn_buffers = NULL;
QUIC_ADDR Address = {};
int UdpPort = 0;

Expand Down Expand Up @@ -562,11 +564,6 @@ start_listener3(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
return ERROR_TUPLE_2(ATOM_BADARG);
}

if (!load_alpn(env, &options, &alpn_buffer_length, alpn_buffers))
{
return ERROR_TUPLE_2(ATOM_ALPN);
}

QuicerConfigCTX *new_config_ctx = init_config_ctx();
if (!new_config_ctx)
{
Expand Down Expand Up @@ -620,23 +617,23 @@ start_listener3(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
l_ctx->config_resource = new_config_ctx;

#if defined(QUICER_USE_TRUSTED_STORE)
/*@
FIXME: ongoing handshake will get segfault if we free it
free(l_ctx->trusted_store)
So, currently we allow leakage, possible solutions are:
a. need some refcnt for trusted_store
b. move trusted_store to config_resource
c. remove trusted_store (most likely)
*/
X509_STORE_free(l_ctx->trusted_store);
l_ctx->trusted_store = trusted_store;
#endif // QUICER_USE_TRUSTED_STORE
// No we swap the config
// Now we swap the config

if (!load_alpn(env, &options, &alpn_buffer_length, &alpn_buffers))
{
enif_release_resource(new_config_ctx);
ret = ERROR_TUPLE_2(ATOM_ALPN);
goto exit;
}
Status = MsQuic->ListenerStart(
l_ctx->Listener, alpn_buffers, alpn_buffer_length, &Address);

free_alpn_buffers(alpn_buffers, alpn_buffer_length);

if (QUIC_FAILED(
Status = MsQuic->ListenerStart(
l_ctx->Listener, alpn_buffers, alpn_buffer_length, &Address)))
if (QUIC_FAILED(Status))
{
TP_NIF_3(start_fail, (uintptr_t)(l_ctx->Listener), Status);
ret = ERROR_TUPLE_3(ATOM_LISTENER_START_ERROR, ATOM_STATUS(Status));
Expand Down
2 changes: 1 addition & 1 deletion src/quicer.erl
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ start_listener(Listener, Port, Options) ->
quicer_nif:start_listener(Listener, Port, Options).

%% @doc Stop a started listener which could be closed or restarted later.
-spec stop_listener(listener_handle()) -> ok.
-spec stop_listener(listener_handle()) -> ok | {error, any()}.
stop_listener(Handle) ->
case quicer_nif:stop_listener(Handle) of
ok ->
Expand Down
56 changes: 52 additions & 4 deletions src/quicer_listener.erl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@
-export([
start_link/3,
start_listener/3,
stop_listener/1
stop_listener/1,
lock/2,
unlock/2,
reload/2,
get_handle/2
]).

%% gen_server callbacks
Expand All @@ -35,9 +39,11 @@

-record(state, {
name :: atom(),
listen_on :: quicer:listen_on(),
listener :: quicer:listener_handle(),
conn_sup :: pid(),
alpn :: [string()]
alpn :: [string()],
opts :: quicer:listener_opts()
}).

-export_type([listener_name/0]).
Expand Down Expand Up @@ -72,6 +78,26 @@ start_listener(Name, ListenOn, Options) ->
stop_listener(Name) ->
quicer_listener_sup:stop_listener(Name).

-spec lock(pid(), timeout()) -> ok | {error, _}.
lock(Pid, Timeout) ->
gen_server:call(Pid, lock, Timeout).

-spec unlock(pid(), timeout()) -> ok | {error, _}.
unlock(Pid, Timeout) ->
gen_server:call(Pid, unlock, Timeout).

%% @doc Reload the listener with new *listener* opts.
%% @NOTE: the acceptor opts and stream opts are not reloaded.
%%% if you want to reload them, you should restart the listener (terminate and spawn).
%% @end
-spec reload(pid(), NewConf :: map()) -> ok | {error, _}.
reload(Pid, NewConf) ->
gen_server:call(Pid, {reload, NewConf}, infinity).

-spec get_handle(pid(), timeout()) -> quicer:listener_handle().
get_handle(Pid, Timeout) ->
gen_server:call(Pid, get_handle, Timeout).

%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
Expand All @@ -98,8 +124,10 @@ init([Name, ListenOn, {#{conn_acceptors := N, alpn := Alpn} = LOpts, _COpts, _SO
_ = [{ok, _} = supervisor:start_child(ConnSup, [ConnSup]) || _ <- lists:seq(1, N)],
{ok, #state{
name = Name,
listen_on = ListenOn,
listener = L,
conn_sup = ConnSup,
opts = LOpts,
alpn = Alpn
}}.

Expand All @@ -118,8 +146,28 @@ init([Name, ListenOn, {#{conn_acceptors := N, alpn := Alpn} = LOpts, _COpts, _SO
| {noreply, NewState :: term(), hibernate}
| {stop, Reason :: term(), Reply :: term(), NewState :: term()}
| {stop, Reason :: term(), NewState :: term()}.
handle_call(_Request, _From, State) ->
Reply = ok,
handle_call(get_handle, _From, State) ->
{reply, {ok, State#state.listener}, State};
handle_call(lock, _From, State) ->
Res = quicer:stop_listener(State#state.listener),
{reply, Res, State};
handle_call(unlock, _From, State) ->
Res = quicer:start_listener(
State#state.listener,
State#state.listen_on,
State#state.opts
),
{reply, Res, State};
handle_call({reload, NewConf}, _From, State) ->
_ = quicer:stop_listener(State#state.listener),
Res = quicer:start_listener(
State#state.listener,
State#state.listen_on,
NewConf
),
{reply, Res, State};
handle_call(Request, _From, State) ->
Reply = {error, {unimpl, Request}},
{reply, Reply, State}.

%%--------------------------------------------------------------------
Expand Down
Loading

0 comments on commit 8c56a02

Please sign in to comment.