Skip to content

Commit

Permalink
Merge pull request #231 from qzhuyan/dev/william/list-conns
Browse files Browse the repository at this point in the history
feat: list connections under registration
  • Loading branch information
qzhuyan authored Nov 22, 2023
2 parents a3a008a + 3e48c9e commit d58d8fe
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 2 deletions.
61 changes: 61 additions & 0 deletions c_src/quicer_connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ limitations under the License.
#include <unistd.h>

extern QuicerRegistrationCTX *G_r_ctx;
extern pthread_mutex_t GRegLock;

#if defined(DEBUG) && !defined(QUICER_LOGGING_STDOUT)
extern inline void
Expand Down Expand Up @@ -606,6 +607,15 @@ open_connectionX(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
}

CxPlatRefInitialize(&(c_ctx->ref_count));

// link to registration
if (r_ctx)
{
enif_mutex_lock(r_ctx->lock);
CxPlatListInsertTail(&r_ctx->Connections, &c_ctx->RegistrationLink);
enif_mutex_unlock(r_ctx->lock);
}

eHandle = enif_make_resource(env, c_ctx);
return SUCCESS(eHandle);

Expand Down Expand Up @@ -787,6 +797,13 @@ async_connect3(ErlNifEnv *env,
}
}
c_ctx->is_closed = FALSE; // connection opened.
//
if (!is_reuse_handle && r_ctx)
{
enif_mutex_lock(r_ctx->lock);
CxPlatListInsertTail(&r_ctx->Connections, &c_ctx->RegistrationLink);
enif_mutex_unlock(r_ctx->lock);
}

// optional set sslkeylogfile
parse_sslkeylogfile_option(env, eoptions, c_ctx);
Expand Down Expand Up @@ -1670,6 +1687,50 @@ parse_conn_event_mask(ErlNifEnv *env,
return ATOM_OK;
}

ERL_NIF_TERM
get_connectionsX(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
QuicerRegistrationCTX *r_ctx = NULL;
ERL_NIF_TERM res = enif_make_list(env, 0);
if (argc == 0) // use global registration
{
pthread_mutex_lock(&GRegLock);
if (!G_r_ctx)
{
pthread_mutex_unlock(&GRegLock);
return res;
}
r_ctx = G_r_ctx;
}
else
{
if (!enif_get_resource(env, argv[0], ctx_reg_t, (void **)&r_ctx))
{
return ERROR_TUPLE_2(ATOM_BADARG);
}
}
enif_mutex_lock(r_ctx->lock);
CXPLAT_LIST_ENTRY *Entry = r_ctx->Connections.Flink;
while (Entry != &r_ctx->Connections)
{
QuicerConnCTX *c_ctx
= CXPLAT_CONTAINING_RECORD(Entry, QuicerConnCTX, RegistrationLink);
if (get_conn_handle(c_ctx))
{
res = enif_make_list_cell(env, enif_make_resource(env, c_ctx), res);
put_conn_handle(c_ctx);
}
Entry = Entry->Flink;
}
enif_mutex_unlock(r_ctx->lock);

if (argc == 0) // use global registration
{
pthread_mutex_unlock(&GRegLock);
}
return res;
}

///_* Emacs
///====================================================================
/// Local Variables:
Expand Down
3 changes: 3 additions & 0 deletions c_src/quicer_connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,7 @@ QUIC_STATUS continue_connection_handshake(QuicerConnCTX *c_ctx);
ERL_NIF_TERM
peercert1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);

ERL_NIF_TERM
get_connectionsX(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);

#endif // __QUICER_CONNECTION_H_
16 changes: 16 additions & 0 deletions c_src/quicer_ctx.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ init_c_ctx()
c_ctx->is_closed = TRUE; // init
c_ctx->config_resource = NULL;
c_ctx->peer_cert = NULL;
CxPlatListInitializeHead(&c_ctx->RegistrationLink);
return c_ctx;
}

Expand Down Expand Up @@ -188,6 +189,21 @@ destroy_c_ctx(QuicerConnCTX *c_ctx)
X509_STORE_free(c_ctx->trusted);
c_ctx->trusted = NULL;
}

QuicerRegistrationCTX *r_ctx;
if (c_ctx->r_ctx)
{
r_ctx = c_ctx->r_ctx;
}
else
{
r_ctx = G_r_ctx;
}

enif_mutex_lock(r_ctx->lock);
CxPlatListEntryRemove(&c_ctx->RegistrationLink);
enif_mutex_unlock(r_ctx->lock);

enif_demonitor_process(c_ctx->env, c_ctx, &c_ctx->owner_mon);
enif_release_resource(c_ctx);
}
Expand Down
1 change: 1 addition & 0 deletions c_src/quicer_ctx.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ typedef struct QuicerConnCTX
// for client, alloc on its own
QuicerConfigCTX *config_resource;
QuicerRegistrationCTX *r_ctx;
CXPLAT_LIST_ENTRY RegistrationLink;
HQUIC Connection;
QUICER_ACCEPTOR_QUEUE *acceptor_queue;
ACCEPTOR *owner;
Expand Down
18 changes: 18 additions & 0 deletions c_src/quicer_listener.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ ServerListenerCallback(__unused_parm__ HQUIC Listener,
// Note, c_ctx is newly init here, don't grab lock.
//
c_ctx = init_c_ctx();
c_ctx->r_ctx = l_ctx->r_ctx;
ErlNifEnv *env = c_ctx->env;

if (!c_ctx)
Expand Down Expand Up @@ -195,6 +196,23 @@ ServerListenerCallback(__unused_parm__ HQUIC Listener,
(void *)ServerConnectionCallback,
c_ctx);

QuicerRegistrationCTX *r_ctx;
if (l_ctx->r_ctx)
{
r_ctx = l_ctx->r_ctx;
}
else
{
r_ctx = G_r_ctx;
}

if (r_ctx)
{
enif_mutex_lock(r_ctx->lock);
CxPlatListInsertTail(&r_ctx->Connections, &c_ctx->RegistrationLink);
enif_mutex_unlock(r_ctx->lock);
}

c_ctx->is_closed = FALSE; // new connection
enif_clear_env(env);
break;
Expand Down
4 changes: 3 additions & 1 deletion c_src/quicer_nif.c
Original file line number Diff line number Diff line change
Expand Up @@ -1568,7 +1568,9 @@ static ErlNifFunc nif_funcs[] = {
{ "get_conn_rid", 1, get_conn_rid1, 1},
{ "get_stream_rid", 1, get_stream_rid1, 1},
{ "get_listeners", 0, get_listenersX, 0},
{ "get_listeners", 1, get_listenersX, 0}
{ "get_listeners", 1, get_listenersX, 0},
{ "get_connections", 0, get_connectionsX, 0},
{ "get_connections", 1, get_connectionsX, 0},
// clang-format on
};

Expand Down
15 changes: 15 additions & 0 deletions src/quicer.erl
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@
, open_connection/0
, get_listeners/0
, get_listeners/1
, get_connections/0
, get_connections/1
, close_registration/1
]).

Expand Down Expand Up @@ -990,6 +992,19 @@ get_listeners(global) ->
get_listeners(Reg) ->
quicer_nif:get_listeners(Reg).


%% @doc Get a list connections under global registration
-spec get_connections() -> quicer_nif:get_connections().
get_connections() ->
quicer_nif:get_connections().

%% @doc Get a list of connections under registration handle
-spec get_connections(Reg | global) -> quicer_nif:get_connections(Reg).
get_connections(global) ->
quicer_nif:get_connections();
get_connections(Reg) ->
quicer_nif:get_connections(Reg).

%% @doc set controlling process for Connection/Stream.
%% mimic {@link ssl:controlling_process/2}
%% @see wait_for_handoff/2
Expand Down
12 changes: 11 additions & 1 deletion src/quicer_nif.erl
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
, open_connection/1
, get_listeners/0
, get_listeners/1
, get_connections/0
, get_connections/1
]).

-export([abi_version/0]).
Expand Down Expand Up @@ -310,7 +312,15 @@ get_listeners() ->
erlang:nif_error(nif_library_not_loaded).

-spec get_listeners(reg_handle()) -> [listener_handle()] | {error, badarg}.
get_listeners(_RegHandle) ->
get_listeners(_) ->
erlang:nif_error(nif_library_not_loaded).

-spec get_connections() -> [connection_handle()].
get_connections() ->
erlang:nif_error(nif_library_not_loaded).

-spec get_connections(reg_handle()) -> [connection_handle()] | {error, badarg}.
get_connections(_RegHandle) ->
erlang:nif_error(nif_library_not_loaded).

%% Internals
Expand Down
33 changes: 33 additions & 0 deletions test/quicer_connection_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,39 @@ tc_conn_opt_local_uni_stream_count(Config) ->
ct:fail("listener_timeout")
end.

tc_conn_list(Config) ->
Port = select_port(),
Owner = self(),
{SPid, _Ref} = spawn_monitor(fun() -> echo_server(Owner, Config, Port) end),
Reg = proplists:get_value(quic_registration, Config, undefined),
receive
listener_ready ->
case Reg of
undefined ->
?assertEqual(0, length(quicer:get_connections()));
Reg ->
?assertEqual(0, length(quicer:get_connections(Reg)))
end
after 5000 ->
ct:fail("listener_timeout")
end,
{ok, Conn} = quicer:connect("127.0.0.1", Port, default_conn_opts(Config), 5000),
{ok, Stm} = quicer:start_stream(Conn, []),
{ok, 4} = quicer:send(Stm, <<"ping">>),
{ok, Cnt} = quicer:getopt(Conn, param_conn_local_unidi_stream_count),
?assert(is_integer(Cnt)),
Conns = case Reg of
undefined ->
quicer:get_connections();
Reg ->
quicer:get_connections(Reg)
end,
?assertEqual(2, length(Conns)),

{ok, ClientName} = quicer:sockname(Conn),
?assertMatch([{ok, ClientName}, {ok, {_, Port}}],
lists:map(fun quicer:peername/1, Conns)),
SPid ! done.

%%%
%%% Helpers
Expand Down

0 comments on commit d58d8fe

Please sign in to comment.