diff --git a/c_src/quicer_config.c b/c_src/quicer_config.c index dd4b4501..215b8962 100644 --- a/c_src/quicer_config.c +++ b/c_src/quicer_config.c @@ -726,18 +726,17 @@ encode_parm_to_eterm(ErlNifEnv *env, || QUIC_PARAM_GLOBAL_RETRY_MEMORY_PERCENT == Param))) { if (BufferLength == sizeof(uint64_t)) - { - res = SUCCESS(ETERM_UINT_64(*(uint64_t *)Buffer)); - } + { + res = SUCCESS(ETERM_UINT_64(*(uint64_t *)Buffer)); + } else if (BufferLength == sizeof(uint32_t)) - { - res = SUCCESS(ETERM_INT(*(uint32_t *)Buffer)); - } + { + res = SUCCESS(ETERM_INT(*(uint32_t *)Buffer)); + } else if (BufferLength == sizeof(uint16_t)) - { - res = SUCCESS(ETERM_INT(*(uint16_t *)Buffer)); - } - + { + res = SUCCESS(ETERM_INT(*(uint16_t *)Buffer)); + } } else if ((QUICER_PARAM_HANDLE_TYPE_CONN == Type && (QUIC_PARAM_CONN_REMOTE_ADDRESS == Param diff --git a/c_src/quicer_ctx.c b/c_src/quicer_ctx.c index 4d27f64d..4ea8e8fb 100644 --- a/c_src/quicer_ctx.c +++ b/c_src/quicer_ctx.c @@ -53,6 +53,10 @@ deinit_l_ctx(QuicerListenerCTX *l_ctx) void destroy_l_ctx(QuicerListenerCTX *l_ctx) { + // @note, Destroy config asap as it holds rundown + // ref count in registration + destroy_config_ctx(l_ctx->config_resource); + l_ctx->config_resource = NULL; enif_release_resource(l_ctx); } diff --git a/c_src/quicer_ctx.h b/c_src/quicer_ctx.h index 6ee389f0..31237bcc 100644 --- a/c_src/quicer_ctx.h +++ b/c_src/quicer_ctx.h @@ -49,6 +49,7 @@ typedef struct QuicerListenerCTX // Listener handle closed flag // false means the handle is invalid BOOLEAN is_closed; + BOOLEAN is_stopped; BOOLEAN allow_insecure; void *reserved1; void *reserved2; diff --git a/c_src/quicer_listener.c b/c_src/quicer_listener.c index fd25f0cd..f32f0d59 100644 --- a/c_src/quicer_listener.c +++ b/c_src/quicer_listener.c @@ -216,15 +216,6 @@ ServerListenerCallback(__unused_parm__ HQUIC Listener, case QUIC_LISTENER_EVENT_STOP_COMPLETE: env = l_ctx->env; - - // Close listener in NIF CTX leads to NULL Listener HQUIC - assert(l_ctx->Listener == NULL); - - // Dummy call to prevent leakage if handle is not NULL - // @TODO they should be removed when we support ListenerStop call - MsQuic->ListenerClose(l_ctx->Listener); - l_ctx->Listener = NULL; - enif_send(NULL, &(l_ctx->listenerPid), NULL, @@ -232,7 +223,13 @@ ServerListenerCallback(__unused_parm__ HQUIC Listener, ATOM_QUIC, ATOM_LISTENER_STOPPED, enif_make_resource(env, l_ctx))); - is_destroy = TRUE; + if (!l_ctx->Listener) + { + // @NOTE This callback is part of the listener *close* process + // Listener is already closing, we can destroy the l_ctx now. + assert(!l_ctx->is_stopped); + is_destroy = TRUE; + } enif_clear_env(env); break; default: @@ -284,7 +281,6 @@ listen2(ErlNifEnv *env, __unused_parm__ int argc, const ERL_NIF_TERM argv[]) return ERROR_TUPLE_2(ATOM_BADARG); } - // Build CredConfig QUIC_CREDENTIAL_CONFIG CredConfig; CxPlatZeroMemory(&CredConfig, sizeof(QUIC_CREDENTIAL_CONFIG)); @@ -452,6 +448,8 @@ listen2(ErlNifEnv *env, __unused_parm__ int argc, const ERL_NIF_TERM argv[]) l_ctx->Listener, alpn_buffers, alpn_buffer_length, &Address))) { TP_NIF_3(start_fail, (uintptr_t)(l_ctx->Listener), Status); + MsQuic->ListenerClose(l_ctx->Listener); + l_ctx->Listener = NULL; destroy_l_ctx(l_ctx); return ERROR_TUPLE_3(ATOM_LISTENER_START_ERROR, ATOM_STATUS(Status)); } @@ -465,21 +463,140 @@ close_listener1(ErlNifEnv *env, const ERL_NIF_TERM argv[]) { QuicerListenerCTX *l_ctx; + BOOLEAN is_destroy = FALSE; + ERL_NIF_TERM ret = ATOM_OK; if (!enif_get_resource(env, argv[0], ctx_listener_t, (void **)&l_ctx)) { return ERROR_TUPLE_2(ATOM_BADARG); } enif_mutex_lock(l_ctx->lock); + if (l_ctx->is_closed) + { + enif_mutex_unlock(l_ctx->lock); + return ERROR_TUPLE_2(ATOM_CLOSED); + } HQUIC l = l_ctx->Listener; + // set before destroy_l_ctx l_ctx->Listener = NULL; l_ctx->is_closed = TRUE; + + // If is_stopped, it means the listener is already stopped. + // there will be no callback for QUIC_LISTENER_EVENT_STOP_COMPLETE + // so we need to destroy the l_ctx otherwise it will leak. + is_destroy = l_ctx->is_stopped; + enif_mutex_unlock(l_ctx->lock); - // It is safe to close it without holding the lock - // This also ensures no ongoing listener callbacks - // This is a blocking call. @TODO have async version or use dirty scheduler MsQuic->ListenerClose(l); + if (is_destroy) + { + destroy_l_ctx(l_ctx); + } + return ret; +} + +ERL_NIF_TERM +stop_listener1(ErlNifEnv *env, + __unused_parm__ int argc, + const ERL_NIF_TERM argv[]) +{ + QuicerListenerCTX *l_ctx; + ERL_NIF_TERM ret = ATOM_OK; + BOOLEAN is_stopped = FALSE; + assert(argc == 1); + if (!enif_get_resource(env, argv[0], ctx_listener_t, (void **)&l_ctx)) + { + return ERROR_TUPLE_2(ATOM_BADARG); + } + + enif_mutex_lock(l_ctx->lock); + HQUIC l = l_ctx->Listener; + is_stopped = l_ctx->is_stopped; + l_ctx->is_stopped = TRUE; + enif_mutex_unlock(l_ctx->lock); + if (!l) + { + return ERROR_TUPLE_2(ATOM_CLOSED); + } + else if (!is_stopped) + { + // void return + MsQuic->ListenerStop(l); + } + return ret; +} + +ERL_NIF_TERM +start_listener3(ErlNifEnv *env, + __unused_parm__ int argc, + const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM listener_handle = argv[0]; + ERL_NIF_TERM elisten_on = argv[1]; + ERL_NIF_TERM options = argv[2]; + + QuicerListenerCTX *l_ctx; + unsigned alpn_buffer_length = 0; + QUIC_BUFFER alpn_buffers[MAX_ALPN]; + QUIC_ADDR Address = {}; + int UdpPort = 0; + + // Return value + ERL_NIF_TERM ret = ATOM_OK; + QUIC_STATUS Status = QUIC_STATUS_SUCCESS; + + if (!enif_get_resource( + env, listener_handle, ctx_listener_t, (void **)&l_ctx)) + { + return ERROR_TUPLE_2(ATOM_BADARG); + } + + char listen_on[INET6_ADDRSTRLEN + 6] = { 0 }; + if (enif_get_string( + env, elisten_on, listen_on, INET6_ADDRSTRLEN + 6, ERL_NIF_LATIN1) + > 0) + { + if (!(QuicAddr4FromString(listen_on, &Address) + || QuicAddr6FromString(listen_on, &Address))) + { + return ERROR_TUPLE_2(ATOM_BADARG); + } + } + else if (enif_get_int(env, elisten_on, &UdpPort) && UdpPort >= 0) + { + QuicAddrSetFamily(&Address, QUIC_ADDRESS_FAMILY_UNSPEC); + QuicAddrSetPort(&Address, (uint16_t)UdpPort); + } + else + { + return ERROR_TUPLE_2(ATOM_BADARG); + } + + if (!load_alpn(env, &options, &alpn_buffer_length, alpn_buffers)) + { + return ERROR_TUPLE_2(ATOM_ALPN); + } + + enif_mutex_lock(l_ctx->lock); + if (!l_ctx->Listener) + { + ret = ERROR_TUPLE_2(ATOM_CLOSED); + goto exit; + } + + if (QUIC_FAILED( + Status = MsQuic->ListenerStart( + l_ctx->Listener, alpn_buffers, alpn_buffer_length, &Address))) + { + TP_NIF_3(start_fail, (uintptr_t)(l_ctx->Listener), Status); + ret = ERROR_TUPLE_3(ATOM_LISTENER_START_ERROR, ATOM_STATUS(Status)); + goto exit; + } + l_ctx->is_stopped = FALSE; + +exit: + enif_mutex_unlock(l_ctx->lock); - return ATOM_OK; + return ret; } diff --git a/c_src/quicer_listener.h b/c_src/quicer_listener.h index 75c9f2c3..3d1d6444 100644 --- a/c_src/quicer_listener.h +++ b/c_src/quicer_listener.h @@ -25,6 +25,13 @@ QUIC_STATUS ServerListenerCallback(HQUIC Listener, QUIC_LISTENER_EVENT *Event); ERL_NIF_TERM listen2(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); + +ERL_NIF_TERM +start_listener3(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); + +ERL_NIF_TERM +stop_listener1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); + ERL_NIF_TERM close_listener1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); diff --git a/c_src/quicer_nif.c b/c_src/quicer_nif.c index 4f321cf2..92cd3509 100644 --- a/c_src/quicer_nif.c +++ b/c_src/quicer_nif.c @@ -1419,6 +1419,8 @@ static ErlNifFunc nif_funcs[] = { { "reg_open", 1, registration, 0 }, { "reg_close", 0, deregistration, 0 }, { "listen", 2, listen2, 0}, + { "start_listener", 3, start_listener3, 0}, + { "stop_listener", 1, stop_listener1, 0}, { "close_listener", 1, close_listener1, 0}, { "open_connection", 0, open_connection0, 0}, { "async_connect", 3, async_connect3, 0}, diff --git a/src/quicer.erl b/src/quicer.erl index b2bb355e..a240c3cb 100644 --- a/src/quicer.erl +++ b/src/quicer.erl @@ -32,7 +32,10 @@ %% Traffic APIs -export([ listen/2 + , stop_listener/1 + , start_listener/3 , close_listener/1 + , close_listener/2 , connect/4 , async_connect/3 , handshake/1 @@ -107,8 +110,8 @@ , open_connection/0 ]). --export([ start_listener/3 %% start application over quic - , stop_listener/1 +-export([ spawn_listener/3 %% start application over quic + , terminate_listener/1 ]). -type connection_opts() :: proplists:proplist() | quicer_connection:opts(). @@ -168,17 +171,42 @@ reg_open(Profile) -> reg_close() -> quicer_nif:reg_close(). --spec start_listener(Appname :: atom(), listen_on(), +%% @doc Start a stopped listener with listener handle. +-spec start_listener(listener_handle(), listen_on(), listen_opts()) -> + {ok, pid()} | {error, any()}. +start_listener(Listener, Port, Options) when is_list(Options)-> + start_listener(Listener, Port, maps:from_list(Options)); +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. +stop_listener(Handle) -> + case quicer_nif:stop_listener(Handle) of + ok -> + receive + {quic, listener_stopped, Handle} -> + ok + end; + %% @TODO handle already stopped + {error, Reason} -> + {error, Reason} + end. + + +%% @doc start a listener process under supervisor tree +-spec spawn_listener(Appname :: atom() | listener_handle(), listen_on(), {listener_opts(), connection_opts(), stream_opts() | user_opts()} ) -> {ok, pid()} | {error, any()}. -start_listener(AppName, Port, Options) -> +spawn_listener(AppName, Port, Options) when is_atom(AppName) -> quicer_listener:start_listener(AppName, Port, Options). --spec stop_listener(atom()) -> ok. -stop_listener(AppName) -> +%% @doc terminate a listener process under supervisor tree +-spec terminate_listener(atom() | listener_handle()) -> ok. +terminate_listener(AppName) when is_atom(AppName)-> quicer_listener:stop_listener(AppName). %% @doc Start listen on Port or "HOST:PORT". @@ -202,9 +230,30 @@ listen(ListenOn, Opts) when is_map(Opts) -> quicer_nif:listen(ListenOn, Opts). %% @doc close listener with listener handle --spec close_listener(listener_handle()) -> ok. +-spec close_listener(listener_handle()) -> ok | {error, badarg | closed | timeout}. close_listener(Listener) -> - quicer_nif:close_listener(Listener). + close_listener(Listener, 5000). + +-spec close_listener(listener_handle(), timer:time()) -> + ok | {error, badarg | closed | timeout}. +close_listener(Listener, Timeout) -> + case quicer_nif:close_listener(Listener) of + ok when Timeout == 0 -> + ok; + ok -> + receive + {quic, listener_stopped, Listener} -> + ok + after Timeout -> + {error, timeout} + end; + {error, closed} -> + %% already closed + %% follow OTP behavior + ok; + {error, _} = E-> + E + end. %% @doc %% Initiate New Connection (Client) diff --git a/src/quicer_nif.erl b/src/quicer_nif.erl index 69531fd2..c682876f 100644 --- a/src/quicer_nif.erl +++ b/src/quicer_nif.erl @@ -20,6 +20,8 @@ , reg_open/1 , reg_close/0 , listen/2 + , start_listener/3 + , stop_listener/1 , close_listener/1 , async_connect/3 , async_accept/2 @@ -113,10 +115,18 @@ reg_close() -> listen(_ListenOn, _Options) -> erlang:nif_error(nif_library_not_loaded). --spec close_listener(listener_handle()) -> ok. +-spec start_listener(listener_handle(), listen_on(), listen_opts()) -> ok | {error, closed | badarg}. +start_listener(_Listener, _ListenOn, _Opts) -> + erlang:nif_error(nif_library_not_loaded). + +-spec close_listener(listener_handle()) -> ok | {error, closed | badarg}. close_listener(_Listener) -> erlang:nif_error(nif_library_not_loaded). +-spec stop_listener(listener_handle()) -> ok | {error, closed | badarg}. +stop_listener(_Listener) -> + erlang:nif_error(nif_library_not_loaded). + -spec open_connection() -> {ok, connection_handle()} | {error, atom_reason()}. open_connection() -> erlang:nif_error(nif_library_not_loaded). diff --git a/test/quicer_SUITE.erl b/test/quicer_SUITE.erl index e86a2183..a62c31f0 100644 --- a/test/quicer_SUITE.erl +++ b/test/quicer_SUITE.erl @@ -58,6 +58,8 @@ , tc_open_listener_inval_cacertfile_2/1 , tc_open_listener_inval_cacertfile_3/1 , tc_start_listener_alpn_too_long/1 + , tc_stop_start_listener/1 + , tc_stop_close_listener/1 , tc_close_listener/1 , tc_close_listener_twice/1 , tc_close_listener_dealloc/1 @@ -274,7 +276,7 @@ end_per_testcase(tc_open_listener_neg_1, _Config) -> quicer:open_lib(), quicer:reg_open(); end_per_testcase(_TestCase, _Config) -> - quicer:stop_listener(mqtt), + quicer:terminate_listener(mqtt), Unhandled = quicer_test_lib:receive_all(), Unhandled =/= [] andalso ct:comment("What left in the message queue: ~p", [Unhandled]), ok. @@ -380,9 +382,9 @@ tc_open_listener_inval_cacertfile_1(Config) -> tc_open_listener_inval_cacertfile_2(Config) -> Port = select_port(), - ?assertMatch({ok, _}, - quicer:listen(Port, [ {cacertfile, [1,2,3,4]} - | default_listen_opts(Config)])), + {ok, L} = quicer:listen(Port, [ {cacertfile, [1,2,3,4]} + | default_listen_opts(Config)]), + ok = quicer:close_listener(L), ok. tc_open_listener_inval_cacertfile_3(Config) -> @@ -409,7 +411,8 @@ tc_open_listener_with_cert_password(Config) -> , {keyfile, filename:join(DataDir, "server-password.key")} , {password, ?SERVER_KEY_PASSWORD} ], - {ok, _L} = quicer:listen(Port, default_listen_opts(PasswordCerts ++ Config)), + {ok, L} = quicer:listen(Port, default_listen_opts(PasswordCerts ++ Config)), + quicer:close_listener(L), ok. tc_open_listener_with_wrong_cert_password(Config) -> @@ -473,23 +476,39 @@ tc_get_listener_opt_stats(Config) -> quicer:close_listener(L). tc_close_listener(_Config) -> - {error,badarg} = quicer:close_listener(make_ref()). + {error, badarg} = quicer:close_listener(make_ref()). tc_close_listener_twice(Config) -> Port = select_port(), {ok, L} = quicer:listen(Port, default_listen_opts(Config)), - quicer:close_listener(L), - quicer:close_listener(L). + ok = quicer:close_listener(L), + %% follow OTP behavior, already closed + ok = quicer:close_listener(L). tc_close_listener_dealloc(Config) -> Port = select_port(), {Pid, Ref} = spawn_monitor(fun() -> - {ok, _L} = quicer:listen(Port, default_listen_opts(Config)) + {ok, L} = quicer:listen(Port, default_listen_opts(Config)), + exit(L) end), - receive {'DOWN', Ref, process, Pid, normal} -> - ok + receive {'DOWN', Ref, process, Pid, L} -> + quicer:close_listener(L) end. +tc_stop_start_listener(Config) -> + Port = select_port(), + LConf = default_listen_opts(Config), + {ok, L} = quicer:listen(Port, LConf), + ok = quicer:stop_listener(L), + ok = quicer:start_listener(L, Port, LConf), + ok = quicer:close_listener(L). + +tc_stop_close_listener(Config) -> + Port = select_port(), + {ok, L} = quicer:listen(Port, default_listen_opts(Config)), + ok = quicer:stop_listener(L), + ok = quicer:close_listener(L, 0). + tc_start_listener_alpn_too_long(Config) -> Port = select_port(), {Pid, Ref} = @@ -506,7 +525,8 @@ tc_start_acceptor_without_callback(Config) -> Port = select_port(), {ok, L} = quicer:listen(Port, default_listen_opts(Config)), ?assertEqual({error, missing_conn_callback}, - quicer_connection:start_link(undefined, L, {[],[],[]}, self())). + quicer_connection:start_link(undefined, L, {[],[],[]}, self())), + quicer:close_listener(L). tc_get_listeners(Config) -> ListenerOpts = [{conn_acceptors, 32} | default_listen_opts(Config)], @@ -522,14 +542,14 @@ tc_get_listeners(Config) -> ], Res = lists:map( fun({Alpn, ListenOn}) -> - {ok, L} = quicer:start_listener( + {ok, L} = quicer:spawn_listener( Alpn, ListenOn, {ListenerOpts, ConnectionOpts, StreamOpts}), L end, Listeners), ?assertEqual(lists:reverse(lists:zip(Listeners, Res)), quicer:listeners()), - lists:foreach(fun({L, _}) -> ok = quicer:stop_listener(L) end, Listeners). + lists:foreach(fun({L, _}) -> ok = quicer:terminate_listener(L) end, Listeners). tc_get_listener(Config) -> ListenerOpts = [{conn_acceptors, 32} | default_listen_opts(Config)], @@ -544,7 +564,7 @@ tc_get_listener(Config) -> , {alpn4, "[::1]:24570"} ], lists:map(fun({Alpn, ListenOn}) -> - {ok, L} = quicer:start_listener(Alpn, ListenOn, + {ok, L} = quicer:spawn_listener(Alpn, ListenOn, {ListenerOpts, ConnectionOpts, StreamOpts}), L end, Listeners), @@ -555,7 +575,7 @@ tc_get_listener(Config) -> true = is_process_alive(LPid) end, Listeners), - lists:foreach(fun({L, _}) -> ok = quicer:stop_listener(L) end, Listeners), + lists:foreach(fun({L, _}) -> ok = quicer:terminate_listener(L) end, Listeners), lists:foreach(fun({Name, _} = NameListenON) -> ?assertEqual({error, not_found}, quicer:listener(Name)), @@ -2256,7 +2276,7 @@ tc_app_echo_server(Config) -> | default_stream_opts() ], Options = {ListenerOpts, ConnectionOpts, StreamOpts}, ct:pal("Listener Options: ~p", [Options]), - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, default_conn_opts(), 5000), {ok, Stm} = quicer:start_stream(Conn, [{active, false}]), {ok, 4} = quicer:async_send(Stm, <<"ping">>), @@ -2265,10 +2285,10 @@ tc_app_echo_server(Config) -> {ok, <<"pingpingping">>} = quicer:recv(Stm, 12), ok = quicer:close_stream(Stm), ok = quicer:close_connection(Conn), - ok = quicer:stop_listener(mqtt), + ok = quicer:terminate_listener(mqtt), %% test that listener could be reopened - {ok, _} = quicer:start_listener(mqtt, Port, Options), - ok = quicer:stop_listener(mqtt), + {ok, _} = quicer:spawn_listener(mqtt, Port, Options), + ok = quicer:terminate_listener(mqtt), ok. tc_strm_opt_active_1(Config) -> @@ -2282,7 +2302,7 @@ tc_strm_opt_active_1(Config) -> | default_stream_opts() ], Options = {ListenerOpts, ConnectionOpts, StreamOpts}, ct:pal("Listener Options: ~p", [Options]), - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, default_conn_opts(), 5000), {ok, Stm} = quicer:start_stream(Conn, [{active, 1}]), {ok, 5} = quicer:send(Stm, <<"ping1">>), @@ -2308,7 +2328,7 @@ tc_strm_opt_active_n(Config) -> | default_stream_opts() ], Options = {ListenerOpts, ConnectionOpts, StreamOpts}, ct:pal("Listener Options: ~p", [Options]), - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, default_conn_opts(), 5000), {ok, Stm} = quicer:start_stream(Conn, [{active, 3}]), {ok, 5} = quicer:async_send(Stm, <<"ping1">>), @@ -2341,7 +2361,7 @@ tc_strm_opt_active_once(Config) -> | default_stream_opts() ], Options = {ListenerOpts, ConnectionOpts, StreamOpts}, ct:pal("Listener Options: ~p", [Options]), - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, default_conn_opts(), 5000), {ok, Stm} = quicer:start_stream(Conn, [{active, once}]), {ok, 5} = quicer:async_send(Stm, <<"ping1">>), @@ -2369,7 +2389,7 @@ tc_strm_opt_active_badarg(Config) -> | default_stream_opts() ], Options = {ListenerOpts, ConnectionOpts, StreamOpts}, ct:pal("Listener Options: ~p", [Options]), - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, default_conn_opts(), 5000), {error, badarg} = quicer:start_stream(Conn, [{active, twice}]). @@ -2384,7 +2404,7 @@ tc_get_conn_rid(Config) -> | default_stream_opts() ], Options = {ListenerOpts, ConnectionOpts, StreamOpts}, ct:pal("Listener Options: ~p", [Options]), - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, default_conn_opts(), 5000), {ok, Rid} = quicer:get_conn_rid(Conn), ?assert(is_integer(Rid) andalso Rid =/=0). @@ -2400,7 +2420,7 @@ tc_get_stream_rid(Config) -> | default_stream_opts() ], Options = {ListenerOpts, ConnectionOpts, StreamOpts}, ct:pal("Listener Options: ~p", [Options]), - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, default_conn_opts(), 5000), {ok, Stm} = quicer:start_stream(Conn, [{active, 3}]), {ok, Rid} = quicer:get_stream_rid(Stm), @@ -2422,7 +2442,7 @@ tc_stream_open_flag_unidirectional(Config) -> | default_stream_opts() ], Options = {ListenerOpts, ConnectionOpts, StreamOpts}, ct:pal("Listener Options: ~p", [Options]), - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, default_conn_opts(), 5000), {ok, Stm} = quicer:start_stream(Conn, [ {active, 3} , {open_flag, ?QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL} @@ -2449,7 +2469,7 @@ tc_stream_start_flag_fail_blocked(Config) -> | default_stream_opts() ], Options = {ListenerOpts, ConnectionOpts, StreamOpts}, ct:pal("Listener Options: ~p", [Options]), - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, default_conn_opts(), 5000), {ok, Stm} = quicer:start_stream(Conn, [ {active, 3}, {start_flag, ?QUIC_STREAM_START_FLAG_FAIL_BLOCKED} , {quic_event_mask, ?QUICER_STREAM_EVENT_MASK_START_COMPLETE} @@ -2493,7 +2513,7 @@ tc_stream_start_flag_immediate(Config) -> | default_stream_opts() ], Options = {ListenerOpts, ConnectionOpts, StreamOpts}, ct:pal("Listener Options: ~p", [Options]), - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, default_conn_opts(), 5000), {ok, Stm} = quicer:start_stream(Conn, [ {active, 3}, {start_flag, ?QUIC_STREAM_START_FLAG_IMMEDIATE} , {quic_event_mask, ?QUICER_STREAM_EVENT_MASK_START_COMPLETE} @@ -2523,7 +2543,7 @@ tc_stream_start_flag_shutdown_on_fail(Config) -> | default_stream_opts() ], Options = {ListenerOpts, ConnectionOpts, StreamOpts}, ct:pal("Listener Options: ~p", [Options]), - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, default_conn_opts(), 5000), {ok, Stm} = quicer:start_stream(Conn, [ {active, 3}, {start_flag, ?QUIC_STREAM_START_FLAG_SHUTDOWN_ON_FAIL bor ?QUIC_STREAM_START_FLAG_FAIL_BLOCKED @@ -2572,7 +2592,7 @@ tc_stream_start_flag_indicate_peer_accept_1(Config) -> | default_stream_opts() ], Options = {ListenerOpts, ConnectionOpts, StreamOpts}, ct:pal("Listener Options: ~p", [Options]), - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, default_conn_opts(), 5000), {ok, Stm} = quicer:start_stream(Conn, [ {active, 3}, {start_flag, ?QUIC_STREAM_START_FLAG_INDICATE_PEER_ACCEPT} , {quic_event_mask, ?QUICER_STREAM_EVENT_MASK_START_COMPLETE} @@ -2787,7 +2807,7 @@ tc_conn_opt_sslkeylogfile(Config) -> | default_stream_opts() ], Options = {ListenerOpts, ConnectionOpts, StreamOpts}, ct:pal("Listener Options: ~p", [Options]), - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, [ {sslkeylogfile, TargetFName} | default_conn_opts() ], @@ -2808,7 +2828,7 @@ tc_insecure_traffic(Config) -> | default_stream_opts() ], Options = {ListenerOpts, ConnectionOpts, StreamOpts}, ct:pal("Listener Options: ~p", [Options]), - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, [{param_conn_disable_1rtt_encryption, true} | default_conn_opts()], 5000), @@ -2837,7 +2857,7 @@ tc_event_start_compl_client(Config) -> | default_stream_opts() ], Options = {ListenerOpts, ConnectionOpts, StreamOpts}, ct:pal("Listener Options: ~p", [Options]), - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, [{param_conn_disable_1rtt_encryption, true} | default_conn_opts()], 5000), @@ -2879,7 +2899,7 @@ tc_event_start_compl_server(Config) -> | default_stream_opts() ], Options = {ListenerOpts, ConnectionOpts, StreamOpts}, ct:pal("Listener Options: ~p", [Options]), - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, [ {param_conn_disable_1rtt_encryption, true} | default_conn_opts()], 5000), @@ -2928,7 +2948,7 @@ tc_direct_send_over_conn(Config) -> | default_stream_opts() ], Options = {ListenerOpts, ConnectionOpts, StreamOpts}, ct:pal("Listener Options: ~p", [Options]), - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:async_connect("localhost", Port, [{param_conn_disable_1rtt_encryption, true} | default_conn_opts()]), @@ -2994,7 +3014,7 @@ tc_direct_send_over_conn_block(Config) -> | default_stream_opts() ], Options = {ListenerOpts, ConnectionOpts, StreamOpts}, ct:pal("Listener Options: ~p", [Options]), - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:async_connect("localhost", Port, [{param_conn_disable_1rtt_encryption, true} | default_conn_opts()]), @@ -3051,7 +3071,7 @@ tc_direct_send_over_conn_fail(Config) -> | default_stream_opts() ], Options = {ListenerOpts, ConnectionOpts, StreamOpts}, ct:pal("Listener Options: ~p", [Options]), - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:async_connect("localhost", Port, [{param_conn_disable_1rtt_encryption, true} | default_conn_opts()]), @@ -3408,6 +3428,7 @@ simple_conn_server(Owner, Config, Port) -> simple_conn_server_loop(L, Conn, Owner) -> receive done -> + quicer:close_connection(Conn), quicer:close_listener(L), ok; peercert -> diff --git a/test/quicer_snb_SUITE.erl b/test/quicer_snb_SUITE.erl index 96883847..9c5ee898 100644 --- a/test/quicer_snb_SUITE.erl +++ b/test/quicer_snb_SUITE.erl @@ -146,7 +146,7 @@ init_per_testcase(_TestCase, Config) -> %% @end %%-------------------------------------------------------------------- end_per_testcase(_TestCase, _Config) -> - quicer:stop_listener(mqtt), + quicer:terminate_listener(mqtt), snabbkaffe:cleanup(), Unhandled = quicer_test_lib:receive_all(), Unhandled =/= [] andalso ct:comment("What left in the message queue: ~p", [Unhandled]), @@ -272,7 +272,7 @@ tc_app_echo_server(Config) -> quicer:close_stream(Stm), quicer:close_connection(Conn), - ok = quicer:stop_listener(mqtt). + ok = quicer:terminate_listener(mqtt). tc_slow_conn(Config) -> Port = select_port(), @@ -296,7 +296,7 @@ tc_slow_conn(Config) -> ct:pal("closing conn"), quicer:close_connection(Conn), ct:pal("stop listener"), - ok = quicer:stop_listener(mqtt) + ok = quicer:terminate_listener(mqtt) end, fun(Result, Trace) -> ct:pal("Trace is ~p", [Trace]), @@ -350,7 +350,7 @@ tc_stream_owner_down(Config) -> function := "ClientStreamCallback", mark := ?QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE, tag := "event"}, 1000, 1000)), ct:pal("stop listener"), - ok = quicer:stop_listener(mqtt) + ok = quicer:terminate_listener(mqtt) end, fun(Result, Trace) -> ct:pal("Trace is ~p", [Trace]), @@ -437,7 +437,7 @@ tc_conn_owner_down(Config) -> ?block_until( #{?snk_kind := debug, event := closed, module := quicer_connection}, 1000, 1000)), ct:pal("stop listener"), - ok = quicer:stop_listener(mqtt), + ok = quicer:terminate_listener(mqtt), {ok, CRid} = quicer:get_conn_rid(Conn), {CRid, SRid} end, @@ -551,7 +551,7 @@ tc_conn_close_flag_1(Config) -> , mark := ?QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE , tag := "event"}, 1000, 3000), ct:pal("stop listener"), - ok = quicer:stop_listener(mqtt), + ok = quicer:terminate_listener(mqtt), {CRid, SRid} end, fun({_CRid, SRid}, Trace) -> @@ -602,7 +602,7 @@ tc_conn_close_flag_2(Config) -> , mark := ?QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE , tag := "event"}, 3000, 3000), %% assume idle_timeout_is 5s ct:pal("stop listener"), - ok = quicer:stop_listener(mqtt) + ok = quicer:terminate_listener(mqtt) end, fun(Result, Trace) -> ct:pal("Trace is ~p", [Trace]), @@ -647,7 +647,7 @@ tc_stream_close_errno(Config) -> , error_code := 1234 }, 1000, 1000), ct:pal("stop listener"), - ok = quicer:stop_listener(mqtt) + ok = quicer:terminate_listener(mqtt) end, fun(Result, Trace) -> ct:pal("Trace is ~p", [Trace]), @@ -869,7 +869,7 @@ tc_conn_idle_close(Config) -> Trace)) end), ct:pal("stop listener"), - ok = quicer:stop_listener(mqtt), + ok = quicer:terminate_listener(mqtt), ok. tc_conn_gc(Config) -> @@ -988,7 +988,7 @@ tc_conn_gc(Config) -> ) end), ct:pal("stop listener"), - ok = quicer:stop_listener(mqtt), + ok = quicer:terminate_listener(mqtt), ok. @@ -1087,7 +1087,7 @@ tc_conn_no_gc(Config) -> ?assert(Conn =/= undefined) end), ct:pal("stop listener"), - ok = quicer:stop_listener(mqtt), + ok = quicer:terminate_listener(mqtt), ok. tc_conn_no_gc_2(Config) -> @@ -1204,7 +1204,7 @@ tc_conn_no_gc_2(Config) -> } = E <- TraceEvents, Rid == CRid])) end), ct:pal("stop listener"), - ok = quicer:stop_listener(mqtt), + ok = quicer:terminate_listener(mqtt), ok. %%% Resume connection with connection opt: `nst' @@ -1252,7 +1252,7 @@ tc_conn_resume_nst(Config) -> {ok, <<"ping3">>} = quicer:recv(Stm2, 5), quicer:shutdown_connection(Conn), ct:pal("stop listener"), - ok = quicer:stop_listener(mqtt) + ok = quicer:terminate_listener(mqtt) end, fun(Result, Trace) -> ct:pal("Trace is ~p", [Trace]), @@ -1325,7 +1325,7 @@ tc_conn_resume_nst_with_stream(Config) -> {ok, <<"ping3">>} = quicer:recv(Stm2, 5), quicer:shutdown_connection(ConnResumed), ct:pal("stop listener"), - ok = quicer:stop_listener(mqtt) + ok = quicer:terminate_listener(mqtt) end, fun(Result, Trace) -> ct:pal("Trace is ~p", [Trace]), @@ -1398,7 +1398,7 @@ tc_conn_resume_nst_async(Config) -> {ok, <<"ping3">>} = quicer:recv(Stm2, 5), ct:pal("stop listener"), quicer:shutdown_connection(ConnResumed), - ok = quicer:stop_listener(mqtt) + ok = quicer:terminate_listener(mqtt) end, fun(Result, Trace) -> ct:pal("Trace is ~p", [Trace]), @@ -1472,7 +1472,7 @@ tc_conn_resume_nst_async_2(Config) -> {ok, <<"ping3">>} = quicer:recv(Stm2, 5), ct:pal("stop listener"), quicer:shutdown_connection(ConnResumed), - ok = quicer:stop_listener(mqtt) + ok = quicer:terminate_listener(mqtt) end, fun(Result, Trace) -> ct:pal("Trace is ~p", [Trace]), @@ -1560,7 +1560,7 @@ tc_conn_resume_nst_with_data(Config) -> ?assertNotEqual(NST2, NST), ct:pal("stop listener"), - ok = quicer:stop_listener(mqtt) + ok = quicer:terminate_listener(mqtt) end, fun(Result, Trace) -> ct:pal("Trace is ~p", [Trace]), @@ -1634,7 +1634,7 @@ tc_listener_no_acceptor(Config) -> {error, transport_down, #{status := connection_refused}} = quicer:connect("localhost", Port, default_conn_opts(), 5000), ct:pal("stop listener"), - ok = quicer:stop_listener(mqtt), + ok = quicer:terminate_listener(mqtt), timer:sleep(5000) end, fun(_Result, Trace) -> @@ -1772,7 +1772,7 @@ tc_accept_stream_active_once(Config) -> ct:pal("Listener Options: ~p", [Options]), ?check_trace(#{timetrap => 10000}, begin - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, [{peer_bidi_stream_count, 10}, {peer_unidi_stream_count, 1} | default_conn_opts()], 5000), @@ -1839,7 +1839,7 @@ tc_accept_stream_active_N(Config) -> ct:pal("Listener Options: ~p", [Options]), ?check_trace(#{timetrap => 10000}, begin - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, [{peer_bidi_stream_count, 10}, {peer_unidi_stream_count, 1} | default_conn_opts()], 5000), {ok, Stm} = quicer:start_stream(Conn, [{active, true}]), @@ -1931,7 +1931,7 @@ tc_multi_streams(Config) -> ct:pal("Listener Options: ~p", [Options]), ?check_trace(#{timetrap => 10000}, begin - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, [{peer_bidi_stream_count, 10} | default_conn_opts()], 5000), {ok, Stm} = quicer:start_stream(Conn, [{active, true}]), @@ -1984,7 +1984,7 @@ tc_multi_streams_example_server_1(Config) -> ct:pal("Listener Options: ~p", [Options]), ?check_trace(#{timetrap => 10000}, begin - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, [{peer_bidi_stream_count, 10}, {peer_unidi_stream_count, 1} | default_conn_opts()], 5000), {ok, Stm} = quicer:start_stream(Conn, [{active, true}]), @@ -2079,7 +2079,7 @@ tc_multi_streams_example_server_2(Config) -> ct:pal("Listener Options: ~p", [Options]), ?check_trace(#{timetrap => 10000}, begin - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), ClientConnOpts = [{quic_event_mask, ?QUICER_CONNECTION_EVENT_MASK_NST} | default_conn_opts()], {ok, ClientConnPid} = example_client_connection:start_link("localhost", Port, {ClientConnOpts, default_stream_opts()}), @@ -2139,7 +2139,7 @@ tc_multi_streams_example_server_3(Config) -> ct:pal("Listener Options: ~p", [Options]), ?check_trace(#{timetrap => 10000}, begin - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), ClientConnOpts = [{quic_event_mask, ?QUICER_CONNECTION_EVENT_MASK_NST} | default_conn_opts()], {ok, ClientConnPid} = example_client_connection:start_link("localhost", Port, {ClientConnOpts, default_stream_opts()}), @@ -2300,7 +2300,7 @@ tc_passive_recv_1(Config) -> ct:pal("Listener Options: ~p", [Options]), ?check_trace(#{timetrap => 10000}, begin - {ok, _QuicApp} = quicer:start_listener(mqtt, Port, Options), + {ok, _QuicApp} = quicer:spawn_listener(mqtt, Port, Options), {ok, Conn} = quicer:connect("localhost", Port, [{peer_bidi_stream_count, 10}, {peer_unidi_stream_count, 1} | default_conn_opts()], 5000), {ok, Stm} = quicer:start_stream(Conn, [{active, true}]), @@ -2373,7 +2373,7 @@ select_port()-> quicer_start_listener(Name, Port, Options)-> quicer_start_listener(Name, Port, Options, 10). quicer_start_listener(Name, Port, Options, N) -> - case quicer:start_listener(mqtt, Port, Options) of + case quicer:spawn_listener(mqtt, Port, Options) of {ok, QuicApp} -> {ok, QuicApp}; {error, listener_start_error, address_in_use} when N > 0 -> %% addr in use, retry....