From b2f041c0246262f3e37beeff474df2b4a0ad5431 Mon Sep 17 00:00:00 2001 From: William Yang Date: Mon, 9 Dec 2024 14:17:20 +0100 Subject: [PATCH 1/9] feat: support cust spawn opts --- src/quicer_conn_acceptor_sup.erl | 16 +++++++++++++--- src/quicer_connection.erl | 15 ++++++++++++++- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/quicer_conn_acceptor_sup.erl b/src/quicer_conn_acceptor_sup.erl index 8953d265..6e8aa46f 100644 --- a/src/quicer_conn_acceptor_sup.erl +++ b/src/quicer_conn_acceptor_sup.erl @@ -63,22 +63,32 @@ start_link(ListenerH, ConnOpts) -> {ok, {SupFlags :: supervisor:sup_flags(), [ChildSpec :: supervisor:child_spec()]}} | ignore. init([ListenerH, Opts]) -> + OptsTab = init_opts_tab(Opts), SupFlags = #{ strategy => simple_one_for_one, intensity => 1, period => 5 }, - OneChild = #{ id => ignored, - start => {quicer_connection, start_link, [undefined, ListenerH, Opts]}, + start => {quicer_connection, start_acceptor, [ListenerH, OptsTab]}, restart => temporary, shutdown => 5000, type => worker }, - {ok, {SupFlags, [OneChild]}}. %%%=================================================================== %%% 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}). diff --git a/src/quicer_connection.erl b/src/quicer_connection.erl index ff144f02..cc0108d8 100644 --- a/src/quicer_connection.erl +++ b/src/quicer_connection.erl @@ -153,6 +153,7 @@ -export([ start_link/3, %% for server + start_acceptor/3, start_link/4, get_cb_state/1, merge_cb_state/2, @@ -216,6 +217,13 @@ start_link(undefined, Listener, {_LOpts, COpts, _SOpts} = Opts, Sup) when is_map start_link(CallbackModule, Listener, Opts, Sup) -> gen_server:start_link(?MODULE, [CallbackModule, Listener, Opts, Sup], []). +-define(DEFAULT_SPAWN_OPTS, [{spawn_opt, [link]}]). +%% @doc start acceptor with shared Listener confs +start_acceptor(ListenHandle, Tab, Sup) when is_pid(Sup) -> + [{c_opts, #{conn_callback := CallbackModule} = Conf}] = ets:lookup(Tab, c_opts), + StartOpts = maps:get(spawn_opts, Conf, ?DEFAULT_SPAWN_OPTS), + gen_server:start(?MODULE, [CallbackModule, ListenHandle, Tab, Sup], StartOpts). + -spec get_cb_state(ConnPid :: pid()) -> cb_state() | {error, any()}. get_cb_state(ConnPid) -> gen_server:call(ConnPid, get_cb_state, infinity). @@ -313,7 +321,12 @@ init([CallbackModule, Listener, {_LOpts, COpts, SOpts}, Sup]) when CallbackModul %% ignore, {stop, Reason} ... Other -> Other - end. + end; +init([CallbackModule, Listener, ConfTab, Sup]) -> + [{l_opts, LOpts}] = ets:lookup(ConfTab, l_opts), + [{c_opts, COpts}] = ets:lookup(ConfTab, c_opts), + [{s_opts, SOpts}] = ets:lookup(ConfTab, s_opts), + init([CallbackModule, Listener, {LOpts, COpts, SOpts}, Sup]). %%-------------------------------------------------------------------- %% @private From 642404645378446c47e869d4618616a8f955f973 Mon Sep 17 00:00:00 2001 From: William Yang Date: Mon, 9 Dec 2024 15:57:08 +0100 Subject: [PATCH 2/9] feat: support get/set listener opts --- src/quicer_conn_acceptor_sup.erl | 14 +-------- src/quicer_listener.erl | 51 ++++++++++++++++++++++++++------ test/quicer_listener_SUITE.erl | 20 ++++++++----- 3 files changed, 56 insertions(+), 29 deletions(-) diff --git a/src/quicer_conn_acceptor_sup.erl b/src/quicer_conn_acceptor_sup.erl index 6e8aa46f..81a82451 100644 --- a/src/quicer_conn_acceptor_sup.erl +++ b/src/quicer_conn_acceptor_sup.erl @@ -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, @@ -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}). diff --git a/src/quicer_listener.erl b/src/quicer_listener.erl index 55dcc828..0c6a6c6a 100644 --- a/src/quicer_listener.erl +++ b/src/quicer_listener.erl @@ -26,6 +26,7 @@ unlock/2, reload/2, reload/3, + get_conf/2, get_handle/2, count_conns/1 ]). @@ -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]). @@ -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 %%%=================================================================== @@ -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 }}. @@ -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) -> @@ -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}. @@ -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, @@ -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. diff --git a/test/quicer_listener_SUITE.erl b/test/quicer_listener_SUITE.erl index e19eee88..1a762aaf 100644 --- a/test/quicer_listener_SUITE.erl +++ b/test/quicer_listener_SUITE.erl @@ -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 From 77ed6d874ce2131ec35ffc5f627cfa91df6d3c7a Mon Sep 17 00:00:00 2001 From: William Yang Date: Mon, 9 Dec 2024 21:25:15 +0100 Subject: [PATCH 3/9] feat: defer conn setting to handshake - remove QUIC_SETTINGS from acceptor --- c_src/quicer_connection.c | 88 +++++++++++++++++++-------------------- c_src/quicer_connection.h | 2 +- c_src/quicer_nif.c | 3 +- c_src/quicer_queue.h | 3 -- src/quicer.erl | 36 ++++++++++++++-- src/quicer_connection.erl | 57 +++++++++++++++++++------ src/quicer_listener.erl | 24 +++++++---- src/quicer_nif.erl | 5 +++ test/quicer_snb_SUITE.erl | 2 +- 9 files changed, 146 insertions(+), 74 deletions(-) diff --git a/c_src/quicer_connection.c b/c_src/quicer_connection.c index e0ccd46f..006ef84c 100644 --- a/c_src/quicer_connection.c +++ b/c_src/quicer_connection.c @@ -899,18 +899,13 @@ async_accept2(ErlNifEnv *env, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM listener = argv[0]; - ERL_NIF_TERM conn_opts = argv[1]; + // @NOTE: since 0.2, we ignore argv[1] QuicerListenerCTX *l_ctx = NULL; - ERL_NIF_TERM active_val = ATOM_TRUE; if (!enif_get_resource(env, listener, ctx_listener_t, (void **)&l_ctx)) { return ERROR_TUPLE_2(ATOM_BADARG); } - // Set parm active is optional - enif_get_map_value( - env, conn_opts, ATOM_QUIC_STREAM_OPTS_ACTIVE, &active_val); - ACCEPTOR *acceptor = AcceptorAlloc(); if (!acceptor) { @@ -923,24 +918,11 @@ async_accept2(ErlNifEnv *env, return ERROR_TUPLE_2(ATOM_BAD_PID); } - if (!set_owner_recv_mode(acceptor, env, active_val)) - { - AcceptorDestroy(acceptor); - return ERROR_TUPLE_2(ATOM_BADARG); - } - - if (!create_settings(env, &conn_opts, &acceptor->Settings)) - { - AcceptorDestroy(acceptor); - return ERROR_TUPLE_2(ATOM_PARAM_ERROR); - } - AcceptorEnqueue(l_ctx->acceptor_queue, acceptor); assert(enif_is_process_alive(env, &(acceptor->Pid))); - ERL_NIF_TERM listenHandle = enif_make_resource(env, l_ctx); - return SUCCESS(listenHandle); + return SUCCESS(listener); } ERL_NIF_TERM @@ -1084,40 +1066,28 @@ continue_connection_handshake(QuicerConnCTX *c_ctx) { QUIC_STATUS Status = QUIC_STATUS_SUCCESS; - if (!c_ctx) - { - return QUIC_STATUS_INTERNAL_ERROR; - } - - if (!c_ctx->Connection) - { - return QUIC_STATUS_INVALID_STATE; - } - - if (QUIC_FAILED(Status = MsQuic->ConnectionSetConfiguration( - c_ctx->Connection, c_ctx->config_ctx->Configuration))) - { - return Status; - } + CXPLAT_FRE_ASSERT(c_ctx); + CXPLAT_FRE_ASSERT(c_ctx->Connection); - // Apply connection owners' option overrides - Status = MsQuic->SetParam(c_ctx->Connection, - QUIC_PARAM_CONN_SETTINGS, - sizeof(QUIC_SETTINGS), - &c_ctx->owner->Settings); + Status = MsQuic->ConnectionSetConfiguration( + c_ctx->Connection, c_ctx->config_ctx->Configuration); return Status; } ERL_NIF_TERM -async_handshake_1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +async_handshake_X(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { QuicerConnCTX *c_ctx; QUIC_STATUS Status = QUIC_STATUS_SUCCESS; ERL_NIF_TERM res = ATOM_OK; - CXPLAT_FRE_ASSERT(argc == 1); + CXPLAT_FRE_ASSERT(argc == 1 || argc == 2); + ERL_NIF_TERM econn = argv[0]; - if (!enif_get_resource(env, argv[0], ctx_connection_t, (void **)&c_ctx)) + QUIC_SETTINGS Settings = { 0 }; + ERL_NIF_TERM active_val = ATOM_TRUE; + + if (!enif_get_resource(env, econn, ctx_connection_t, (void **)&c_ctx)) { return ERROR_TUPLE_2(ATOM_BADARG); } @@ -1129,11 +1099,41 @@ async_handshake_1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) return ERROR_TUPLE_2(ATOM_CLOSED); } + if (argc > 1) + { + ERL_NIF_TERM econn_opts = argv[1]; + // Set parm active is optional + enif_get_map_value( + env, econn_opts, ATOM_QUIC_STREAM_OPTS_ACTIVE, &active_val); + + if (!create_settings(env, &econn_opts, &Settings)) + { + res = ERROR_TUPLE_2(ATOM_PARAM_ERROR); + goto exit; + } + + if (!set_owner_recv_mode(c_ctx->owner, env, active_val)) + { + res = ERROR_TUPLE_2(ATOM_BADARG); + goto exit; + } + + // Apply connection owners' option overrides + if (QUIC_FAILED(Status = MsQuic->SetParam(c_ctx->Connection, + QUIC_PARAM_CONN_SETTINGS, + sizeof(QUIC_SETTINGS), + &Settings))) + { + res = ERROR_TUPLE_2(ATOM_STATUS(Status)); + goto exit; + } + } + if (QUIC_FAILED(Status = continue_connection_handshake(c_ctx))) { res = ERROR_TUPLE_2(ATOM_STATUS(Status)); } - +exit: put_conn_handle(c_ctx); return res; } diff --git a/c_src/quicer_connection.h b/c_src/quicer_connection.h index 09323417..8052770b 100644 --- a/c_src/quicer_connection.h +++ b/c_src/quicer_connection.h @@ -48,7 +48,7 @@ ERL_NIF_TERM get_conn_rid1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM -async_handshake_1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); +async_handshake_X(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); QUIC_STATUS continue_connection_handshake(QuicerConnCTX *c_ctx); diff --git a/c_src/quicer_nif.c b/c_src/quicer_nif.c index c130f286..ac2caf31 100644 --- a/c_src/quicer_nif.c +++ b/c_src/quicer_nif.c @@ -1746,7 +1746,8 @@ static ErlNifFunc nif_funcs[] = { { "open_connection", 1, open_connectionX, 0}, { "async_connect", 3, async_connect3, 0}, { "async_accept", 2, async_accept2, 0}, - { "async_handshake", 1, async_handshake_1, 0}, + { "async_handshake", 1, async_handshake_X, 0}, + { "async_handshake", 2, async_handshake_X, 0}, { "async_shutdown_connection", 3, shutdown_connection3, 0}, { "async_accept_stream", 2, async_accept_stream2, 0}, { "start_stream", 2, async_start_stream2, 0}, diff --git a/c_src/quicer_queue.h b/c_src/quicer_queue.h index 96324a7c..2a4f87db 100644 --- a/c_src/quicer_queue.h +++ b/c_src/quicer_queue.h @@ -55,10 +55,7 @@ typedef struct ACCEPTOR ErlNifPid Pid; ACCEPTOR_RECV_MODE active; uint16_t active_count; /* counter for active_n */ - QUIC_SETTINGS Settings; void *reserved1; - void *reserved2; - void *reserved3; } ACCEPTOR; typedef struct AcceptorsQueue diff --git a/src/quicer.erl b/src/quicer.erl index 95e35fa3..e32ae74d 100644 --- a/src/quicer.erl +++ b/src/quicer.erl @@ -45,7 +45,9 @@ async_connect/3, handshake/1, handshake/2, + handshake/3, async_handshake/1, + async_handshake/2, accept/2, accept/3, async_accept/2, @@ -447,14 +449,29 @@ async_connect(Host, Port, Opts) when is_map(Opts) -> handshake(Conn) -> handshake(Conn, 5000). +-spec handshake(connection_handle(), timeout()) -> + {ok, connection_handle()} | {error, any()}. +handshake(Conn, Timeout) -> + case async_handshake(Conn) of + {error, _} = E -> + E; + ok -> + receive + {quic, connected, Conn, _} -> {ok, Conn}; + {quic, closed, Conn, _Flags} -> {error, closed} + after Timeout -> + {error, timeout} + end + end. + %% @doc Complete TLS handshake after accepted a Connection %% @see handshake/2 %% @see async_handshake/1 --spec handshake(connection_handle(), timeout()) -> +-spec handshake(connection_handle(), conn_opts(), timeout()) -> {ok, connection_handle()} | {error, any()}. -handshake(Conn, Timeout) -> - case async_handshake(Conn) of +handshake(Conn, ConnOpts, Timeout) -> + case async_handshake(Conn, ConnOpts) of {error, _} = E -> E; ok -> @@ -467,13 +484,24 @@ handshake(Conn, Timeout) -> end. %% @doc Complete TLS handshake after accepted a Connection. -%% Caller should expect to receive ```{quic, connected, connection_handle()}''' %% %% @see handshake/2 +%% @see async_handshake/2 -spec async_handshake(connection_handle()) -> ok | {error, any()}. async_handshake(Conn) -> quicer_nif:async_handshake(Conn). +%% @doc Complete TLS handshake after accepted a Connection. +%% also set connection options which override the default listener options. +%% +%% @see handshake/2 +%% @see async_handshake/1 +-spec async_handshake(connection_handle(), conn_opts()) -> ok | {error, any()}. +async_handshake(Conn, ConnOpts) when is_list(ConnOpts) -> + async_handshake(Conn, maps:from_list(ConnOpts)); +async_handshake(Conn, ConnOpts) -> + quicer_nif:async_handshake(Conn, ConnOpts). + %% @doc Accept new Connection (Server) %% %% Accept new connection from listener_handle(). diff --git a/src/quicer_connection.erl b/src/quicer_connection.erl index cc0108d8..98951273 100644 --- a/src/quicer_connection.erl +++ b/src/quicer_connection.erl @@ -217,12 +217,13 @@ start_link(undefined, Listener, {_LOpts, COpts, _SOpts} = Opts, Sup) when is_map start_link(CallbackModule, Listener, Opts, Sup) -> gen_server:start_link(?MODULE, [CallbackModule, Listener, Opts, Sup], []). --define(DEFAULT_SPAWN_OPTS, [{spawn_opt, [link]}]). +-define(DEFAULT_ACCEPTOR_START_OPTS, [{spawn_opt, [link]}]). %% @doc start acceptor with shared Listener confs +-spec start_acceptor(listener_handle(), ets:tid(), pid()) -> {ok, pid()} | {error, any()}. start_acceptor(ListenHandle, Tab, Sup) when is_pid(Sup) -> [{c_opts, #{conn_callback := CallbackModule} = Conf}] = ets:lookup(Tab, c_opts), - StartOpts = maps:get(spawn_opts, Conf, ?DEFAULT_SPAWN_OPTS), - gen_server:start(?MODULE, [CallbackModule, ListenHandle, Tab, Sup], StartOpts). + StartOpts = maps:get(proc_start_opts, Conf, ?DEFAULT_ACCEPTOR_START_OPTS), + gen_server:start_link(?MODULE, [CallbackModule, ListenHandle, Tab, Sup], StartOpts). -spec get_cb_state(ConnPid :: pid()) -> cb_state() | {error, any()}. get_cb_state(ConnPid) -> @@ -285,7 +286,8 @@ init([CallbackModule, {Host, Port}, {COpts, SOpts}]) when callback => CallbackModule, conn_opts => COpts, stream_opts => SOpts, - sup => undefined + sup => undefined, + is_server => false }, {ok, Conn} = quicer:async_connect(Host, Port, COpts), State1 = State0#{conn := Conn}, @@ -298,7 +300,7 @@ init([CallbackModule, {Host, Port}, {COpts, SOpts}]) when Other -> Other end; -%% For Server +%% For Server, deprecating since v0.2 init([CallbackModule, Listener, {LOpts, COpts, SOpts}, Sup]) when is_list(COpts) -> init([CallbackModule, Listener, {LOpts, maps:from_list(COpts), SOpts}, Sup]); init([CallbackModule, Listener, {_LOpts, COpts, SOpts}, Sup]) when CallbackModule =/= undefined -> @@ -309,7 +311,8 @@ init([CallbackModule, Listener, {_LOpts, COpts, SOpts}, Sup]) when CallbackModul callback => CallbackModule, conn_opts => COpts, stream_opts => SOpts, - sup => Sup + sup => Sup, + is_server => true }, %% Async Acceptor {ok, Listener} = quicer_nif:async_accept(Listener, COpts), @@ -322,11 +325,26 @@ init([CallbackModule, Listener, {_LOpts, COpts, SOpts}, Sup]) when CallbackModul Other -> Other end; -init([CallbackModule, Listener, ConfTab, Sup]) -> - [{l_opts, LOpts}] = ets:lookup(ConfTab, l_opts), - [{c_opts, COpts}] = ets:lookup(ConfTab, c_opts), - [{s_opts, SOpts}] = ets:lookup(ConfTab, s_opts), - init([CallbackModule, Listener, {LOpts, COpts, SOpts}, Sup]). +%% For Acceptor since v0.2 +init([_, _, _ConfTab, _] = Args) -> + acceptor_init(Args). +acceptor_init([CallbackModule, Listener, ConfTab, Sup]) -> + process_flag(trap_exit, true), + State0 = #{ + listener => Listener, + callback => CallbackModule, + conf_tab => ConfTab, + sup => Sup, + is_server => true, + conn => undefined, + conn_opts => undefined, + stream_opts => undefined, + callback_state => undefined + }, + %% Async Acceptor + NOOP = #{}, + {ok, Listener} = quicer_nif:async_accept(Listener, NOOP), + {ok, State0}. %%-------------------------------------------------------------------- %% @private @@ -400,16 +418,31 @@ handle_cast(_Request, State) -> | {noreply, NewState :: term(), Timeout :: timeout()} | {noreply, NewState :: term(), hibernate} | {stop, Reason :: normal | term(), NewState :: term()}. +%% deprecating this clause handle_info( {quic, new_conn, C, Props}, #{callback := M, sup := Sup, callback_state := CBState} = State -) -> +) when is_map(CBState) -> ?tp_ignore_side_effects_in_prod(debug, #{ module => ?MODULE, conn => C, props => Props, event => new_conn }), %% I become the connection owner, I should start an new acceptor. Sup =/= undefined andalso (catch supervisor:start_child(Sup, [Sup])), default_cb_ret(M:new_conn(C, Props, CBState), State#{conn := C}); +handle_info( + {quic, new_conn, C, Props}, + #{callback := M, sup := Sup, callback_state := undefined, conf_tab := ConfTab} = State +) -> + %% deferred init + COpts = ets:lookup_element(ConfTab, c_opts, 2), + SOpts = ets:lookup_element(ConfTab, s_opts, 2), + Sup =/= undefined andalso (catch supervisor:start_child(Sup, [Sup])), + case M:init(COpts#{stream_opts => SOpts}) of + {ok, CBState0} -> + default_cb_ret(M:new_conn(C, Props, CBState0), State#{conn := C}); + Other -> + Other + end; handle_info( {quic, connected, C, #{is_resumed := IsResumed} = Props}, #{ diff --git a/src/quicer_listener.erl b/src/quicer_listener.erl index 0c6a6c6a..ef9c89f3 100644 --- a/src/quicer_listener.erl +++ b/src/quicer_listener.erl @@ -101,7 +101,8 @@ reload(Pid, NewConf) -> %% @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(), quicer:listen_on(), NewConf :: map()) -> ok | {error, _}. +-spec reload(pid(), quicer:listen_on(), NewConf :: map() | {map(), map(), map()}) -> + ok | {error, _}. reload(Pid, ListenOn, NewConf) -> gen_server:call(Pid, {reload, ListenOn, NewConf}, infinity). @@ -261,7 +262,8 @@ terminate(_Reason, #state{listener = L}) -> _ = quicer:close_listener(L), ok. --spec do_reload(quicer:listen_on(), map(), #state{}) -> {ok | {error, any()}, #state{}}. +-spec do_reload(quicer:listen_on(), map() | {map(), map(), map()}, #state{}) -> + {ok | {error, any()}, #state{}}. do_reload(ListenOn, NewConf, #state{opts_tab = OptsTab} = State) -> _ = quicer:stop_listener(State#state.listener), Res = quicer:start_listener( @@ -271,21 +273,27 @@ do_reload(ListenOn, NewConf, #state{opts_tab = OptsTab} = State) -> ), case Res of ok -> - true = ets:insert(OptsTab, {l_opts, to_map(NewConf)}), + true = conf_tab_refresh(OptsTab, NewConf), {ok, State#state{listen_on = ListenOn}}; Error -> {Error, State} end. -init_opts_tab({LOpts, COpts, SOpts}) -> +init_opts_tab({_LOpts, _COpts, _SOpts} = Opts) -> 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, [ + %% @NOTE: Be careful with the lifecyle of the items in this table. + %% handles in this table cannot be released until table is gone. + true = conf_tab_refresh(Tab, Opts), + Tab. + +conf_tab_refresh(Tab, {LOpts, COpts, SOpts}) -> + ets:insert(Tab, [ {l_opts, to_map(LOpts)}, {c_opts, to_map(COpts)}, {s_opts, to_map(SOpts)} - ]), - Tab. + ]); +conf_tab_refresh(Tab, LOpts) -> + ets:insert(Tab, {l_opts, to_map(LOpts)}). to_map(Opts) when is_list(Opts) -> maps:from_list(Opts); diff --git a/src/quicer_nif.erl b/src/quicer_nif.erl index 37bf8ad5..be95e7b6 100644 --- a/src/quicer_nif.erl +++ b/src/quicer_nif.erl @@ -33,6 +33,7 @@ async_connect/3, async_accept/2, async_handshake/1, + async_handshake/2, async_shutdown_connection/3, async_accept_stream/2, start_stream/2, @@ -276,6 +277,10 @@ async_accept(_Listener, _Opts) -> ok | {error, badarg | atom_reason()}. async_handshake(_Connection) -> erlang:nif_error(nif_library_not_loaded). +-spec async_handshake(connection_handle(), conn_opts()) -> + ok | {error, badarg | atom_reason()}. +async_handshake(_Connection, _ConnOpts) -> + erlang:nif_error(nif_library_not_loaded). -spec async_shutdown_connection(connection_handle(), conn_shutdown_flag(), app_errno()) -> ok | {error, badarg}. diff --git a/test/quicer_snb_SUITE.erl b/test/quicer_snb_SUITE.erl index 4eebf781..59bc1938 100644 --- a/test/quicer_snb_SUITE.erl +++ b/test/quicer_snb_SUITE.erl @@ -353,7 +353,7 @@ tc_slow_conn(Config) -> }, #{ ?snk_kind := debug, - function := "async_handshake_1", + function := "async_handshake_X", tag := "start", mark := 0, resource_id := _Rid From a5851beed98d981acf9bbbd04ed6f7107b5fc363 Mon Sep 17 00:00:00 2001 From: William Yang Date: Tue, 10 Dec 2024 13:15:18 +0100 Subject: [PATCH 4/9] test: add prop test for async_handshake_2 --- test/prop_quic_types.hrl | 36 ++++++++++++++++++++++++++++++ test/prop_stateful_server_conn.erl | 4 ++++ test/quicer_prop_gen.erl | 23 +++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/test/prop_quic_types.hrl b/test/prop_quic_types.hrl index 15323167..66edc253 100644 --- a/test/prop_quic_types.hrl +++ b/test/prop_quic_types.hrl @@ -61,6 +61,7 @@ | {conn_acceptors, non_neg_integer()} | {settings, [quicer_setting()]}. +-define(UINT32_MAX, 4294967295). -type quicer_setting() :: {max_bytes_per_key, uint64()} | {handshake_idle_timeout_ms, uint64()} @@ -95,6 +96,41 @@ | {max_binding_stateless_operations, uint16()} | {stateless_operation_expiration_ms, uint16()}. +%% happy quicer_settings that msquic won't return invalid_param +-type quicer_setting_with_range() :: + {max_bytes_per_key, 0..(4 bsl 34 - 1)} + | {handshake_idle_timeout_ms, 0..(1 bsl 62 - 1)} + | {idle_timeout_ms, 0..(1 bsl 62 - 1)} + | {tls_client_max_send_buffer, uint32()} + | {tls_server_max_send_buffer, uint32()} + | {stream_recv_window_default, 1..?UINT32_MAX} + | {stream_recv_buffer_default, 4096..?UINT32_MAX} + | {conn_flow_control_window, uint32()} + | {max_stateless_operations, uint32()} + | {initial_window_packets, uint32()} + | {send_idle_timeout_ms, uint32()} + | {initial_rtt_ms, 1..?UINT32_MAX} + | {max_ack_delay_ms, 1..(1 bsl 14 - 1)} + | {disconnect_timeout_ms, 1..600000} + | {keep_alive_interval_ms, uint32()} + | {congestion_control_algorithm, 0 | 1} + | {peer_bidi_stream_count, uint16()} + | {peer_unidi_stream_count, uint16()} + | {retry_memory_limit, uint16()} + | {load_balancing_mode, 0..2} + | {max_operations_per_drain, uint8()} + | {send_buffering_enabled, 0 | 1} + | {pacing_enabled, 0 | 1} + | {migration_enabled, 0 | 1} + | {datagram_receive_enabled, 0 | 1} + | {server_resumption_level, 0 | 1 | 2} + | {minimum_mtu, uint16()} + | {maximum_mtu, uint16()} + | {mtu_discovery_search_complete_timeout_us, uint64()} + | {mtu_discovery_missing_probe_count, uint8()} + | {max_binding_stateless_operations, uint16()} + | {stateless_operation_expiration_ms, uint16()}. + -type quicer_conn_opts() :: [conn_opt()]. -type conn_opt() :: {alpn, [string()]} diff --git a/test/prop_stateful_server_conn.erl b/test/prop_stateful_server_conn.erl index a5952ecc..ef292b12 100644 --- a/test/prop_stateful_server_conn.erl +++ b/test/prop_stateful_server_conn.erl @@ -95,6 +95,7 @@ initial_state() -> command(#{handle := Handle}) -> frequency([ {200, {call, quicer, handshake, [Handle, 1000]}}, + {100, {call, quicer, handshake, [Handle, valid_quicer_settings(), 1000]}}, {100, {call, quicer, getopt, [Handle, ?LET({Opt, _}, conn_opt(), Opt)]}}, {100, {call, quicer, async_accept_stream, [Handle, ?LET(Opts, quicer_acceptor_opts(), Opts)]}}, @@ -327,6 +328,9 @@ step_calls(#{calls := Calls} = S) -> S#{calls := Calls + 1}. %%% Generators +valid_quicer_settings() -> + quicer_prop_gen:valid_quicer_settings(). + %%%%%%%%%%%%%%%%%%%%%%% %%% Listener helper %%% %%%%%%%%%%%%%%%%%%%%%%% diff --git a/test/quicer_prop_gen.erl b/test/quicer_prop_gen.erl index c7e47930..a1750d7e 100644 --- a/test/quicer_prop_gen.erl +++ b/test/quicer_prop_gen.erl @@ -32,6 +32,7 @@ valid_reg_handle/0, valid_handle/0, valid_csend_stream_opts/0, + valid_quicer_settings/0, pid/0, data/0, quicer_send_flags/0, @@ -290,6 +291,20 @@ valid_csend_stream_opts() -> ) ). +%% @see msquic/src/core/settings.c +valid_quicer_settings() -> + ?SUCHTHAT( + Opts, + ?LET(Q, list(quicer_setting_with_range()), Q), + %% Conds below from msquic/src/core/settings.c + quicer_setting_val_is_power_2(stream_recv_window_default, Opts) andalso + quicer_setting_val_is_power_2(stream_recv_window_bidi_local_default, Opts) andalso + quicer_setting_val_is_power_2(stream_recv_window_bidi_remote_default, Opts) andalso + quicer_setting_val_is_power_2(stream_recv_window_unidi_default, Opts) andalso + (proplists:get_value(maximum_mtu, Opts, 1500) > + proplists:get_value(minimum_mtu, Opts, 1248)) + ). + -spec ensure_dummy_listener(non_neg_integer()) -> _. ensure_dummy_listener(Port) -> case is_pid(whereis(?dummy_listener)) of @@ -335,3 +350,11 @@ acceptor_loop(L) -> _ -> acceptor_loop(L) end. + +-spec quicer_setting_val_is_power_2(atom(), proplists:proplist()) -> boolean(). +quicer_setting_val_is_power_2(Key, Opts) -> + is_pow_2(maps:get(Key, maps:from_list(Opts), 2)). +is_pow_2(N) when is_integer(N), N > 0 -> + (N band (N - 1)) == 0; +is_pow_2(_) -> + false. From 2ad3d033976aa373a8ccae463cfe33009f96abb6 Mon Sep 17 00:00:00 2001 From: William Yang Date: Wed, 11 Dec 2024 10:04:15 +0100 Subject: [PATCH 5/9] test: update range --- test/prop_quic_types.hrl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/prop_quic_types.hrl b/test/prop_quic_types.hrl index 66edc253..149c29f3 100644 --- a/test/prop_quic_types.hrl +++ b/test/prop_quic_types.hrl @@ -106,7 +106,7 @@ | {stream_recv_window_default, 1..?UINT32_MAX} | {stream_recv_buffer_default, 4096..?UINT32_MAX} | {conn_flow_control_window, uint32()} - | {max_stateless_operations, uint32()} + | {max_stateless_operations, 1..16} | {initial_window_packets, uint32()} | {send_idle_timeout_ms, uint32()} | {initial_rtt_ms, 1..?UINT32_MAX} @@ -129,7 +129,7 @@ | {mtu_discovery_search_complete_timeout_us, uint64()} | {mtu_discovery_missing_probe_count, uint8()} | {max_binding_stateless_operations, uint16()} - | {stateless_operation_expiration_ms, uint16()}. + | {stateless_operation_expiration_ms, 10..(1 bsl 16 -1)}. -type quicer_conn_opts() :: [conn_opt()]. -type conn_opt() :: From 5eade4d1bc2f1189ef822e2bc622fe6aa6b5b582 Mon Sep 17 00:00:00 2001 From: William Yang Date: Wed, 11 Dec 2024 11:25:55 +0100 Subject: [PATCH 6/9] test(test): disable ct log in proper test --- test/example_client_connection.erl | 6 +++--- test/example_client_stream.erl | 18 +++++++++++++----- test/example_server_connection.erl | 3 ++- test/example_server_stream.erl | 11 ++++++----- test/prop_quic_types.hrl | 2 +- test/quicer_connection_SUITE.erl | 2 ++ test/quicer_ct.hrl | 22 ++++++++++++++++++++++ 7 files changed, 49 insertions(+), 15 deletions(-) create mode 100644 test/quicer_ct.hrl diff --git a/test/example_client_connection.erl b/test/example_client_connection.erl index 01bf2bc9..cc83de5c 100644 --- a/test/example_client_connection.erl +++ b/test/example_client_connection.erl @@ -20,7 +20,7 @@ -include_lib("snabbkaffe/include/snabbkaffe.hrl"). -include("quicer_types.hrl"). - +-include("quicer_ct.hrl"). %% API -export([start_link/3]). @@ -83,7 +83,7 @@ new_conn(_Conn, #{version := _Vsn}, #{stream_opts := _SOpts} = S) -> connected(Conn, Flags, #{conn := Conn} = S) -> ?tp(debug, #{module => ?MODULE, conn => Conn, flags => Flags, event => connected}), - ct:pal("~p connected and expecting NST within 100ms", [?MODULE]), + ?LOG("~p connected and expecting NST within 100ms", [?MODULE]), {100, maps:merge(S, Flags)}. resumed(Conn, Data, #{resumed_callback := ResumeFun} = S) when @@ -116,7 +116,7 @@ new_stream( {ok, CBState#{streams := [{E, Stream} | Streams]}} end; Other -> - ct:pal("Start accepting remote stream error ~p", [Other]), + ?LOG("Start accepting remote stream error ~p", [Other]), {ok, CBState#{streams := [{start_error, Stream} | Streams]}} end. diff --git a/test/example_client_stream.erl b/test/example_client_stream.erl index 3323eb9e..8d28b650 100644 --- a/test/example_client_stream.erl +++ b/test/example_client_stream.erl @@ -37,6 +37,7 @@ -export([handle_stream_data/4]). -include("quicer.hrl"). +-include("quicer_ct.hrl"). -include_lib("snabbkaffe/include/snabbkaffe.hrl"). init_handoff(Stream, _StreamOpts, Conn, #{flags := Flags}) -> @@ -47,7 +48,7 @@ init_handoff(Stream, _StreamOpts, Conn, #{flags := Flags}) -> is_local => false, is_unidir => quicer:is_unidirectional(Flags) }, - ct:pal("init_handoff ~p", [{InitState, _StreamOpts}]), + ?LOG("init_handoff ~p", [{InitState, _StreamOpts}]), {ok, InitState}. post_handoff(Stream, _PostData, State) -> @@ -94,11 +95,11 @@ peer_send_shutdown(Stream, _Flags, S) -> send_complete(_Stream, false, S) -> {ok, S}; send_complete(_Stream, true = _IsCanceled, S) -> - ct:pal("~p : send is canceled", [?FUNCTION_NAME]), + ?LOG("~p : send is canceled", [?FUNCTION_NAME]), {ok, S}. send_shutdown_complete(_Stream, _Flags, S) -> - ct:pal("~p : stream send is complete", [?FUNCTION_NAME]), + ?LOG("~p : stream send is complete", [?FUNCTION_NAME]), {ok, S}. start_completed(Stream, #{status := success, stream_id := StreamId} = P, S) -> @@ -114,14 +115,21 @@ start_completed(_Stream, #{status := Other}, S) -> %% Local stream, Unidir handle_stream_data(Stream, Bin, _Flags, #{is_local := true, is_unidir := false} = State) -> ?tp(debug, #{stream => Stream, data => Bin, module => ?MODULE, dir => local_bidir}), - ct:pal("Client recv: ~p from ~p", [Bin, Stream]), + ?LOG("Client recv: ~p from ~p", [Bin, Stream]), {ok, State}; %% Remote stream handle_stream_data( Stream, Bin, _Flags, #{is_local := false, is_unidir := true, conn := _Conn} = State ) -> ?tp(debug, #{stream => Stream, data => Bin, module => ?MODULE, dir => remote_unidir}), - ct:pal("Client recv: ~p from ~p", [Bin, Stream]), + ?LOG("Client recv: ~p from ~p", [Bin, Stream]), + {ok, State}; +handle_stream_data( + Stream, Bin, _Flags, #{is_local := false, is_unidir := false, conn := _Conn} = State +) -> + %% for proper test + ?tp(debug, #{stream => Stream, data => Bin, module => ?MODULE, dir => remote_unidir}), + ?LOG("Client recv: ~p from ~p", [Bin, Stream]), {ok, State}; handle_stream_data( _Stream, diff --git a/test/example_server_connection.erl b/test/example_server_connection.erl index d63a338d..61c24be0 100644 --- a/test/example_server_connection.erl +++ b/test/example_server_connection.erl @@ -29,6 +29,7 @@ -include_lib("snabbkaffe/include/snabbkaffe.hrl"). -include("quicer_types.hrl"). +-include("quicer_ct.hrl"). %% Callback init -export([init/1]). @@ -149,7 +150,7 @@ dgram_recv(C, Bin, _Flag, S) -> case quicer:send_dgram(C, Bin) of {ok, _} -> ok; %% for testing when peer disable the receiving - Error -> ct:pal("send dgram error: ~p~n", [Error]) + Error -> ?LOG("send dgram error: ~p~n", [Error]) end, {ok, S}. diff --git a/test/example_server_stream.erl b/test/example_server_stream.erl index 11b5e6be..6a3549cf 100644 --- a/test/example_server_stream.erl +++ b/test/example_server_stream.erl @@ -39,6 +39,7 @@ -include("quicer.hrl"). -include_lib("snabbkaffe/include/snabbkaffe.hrl"). +-include("quicer_ct.hrl"). init_handoff(Stream, _StreamOpts, Conn, #{flags := Flags}) -> InitState = #{ @@ -48,7 +49,7 @@ init_handoff(Stream, _StreamOpts, Conn, #{flags := Flags}) -> is_local => false, is_unidir => quicer:is_unidirectional(Flags) }, - ct:pal("init_handoff ~p", [{InitState, _StreamOpts}]), + ?LOG("init_handoff ~p", [{InitState, _StreamOpts}]), {ok, InitState}. post_handoff(Stream, _PostData, State) -> @@ -99,11 +100,11 @@ peer_send_shutdown(Stream, _Flags, S) -> send_complete(_Stream, false, S) -> {ok, S}; send_complete(_Stream, true = _IsCanceled, S) -> - ct:pal("~p : send is canceled", [?FUNCTION_NAME]), + ?LOG("~p : send is canceled", [?FUNCTION_NAME]), {ok, S}. send_shutdown_complete(_Stream, _Flags, S) -> - ct:pal("~p : stream send is complete", [?FUNCTION_NAME]), + ?LOG("~p : stream send is complete", [?FUNCTION_NAME]), {ok, S}. start_completed(_Stream, #{status := success, stream_id := StreamId}, S) -> @@ -121,7 +122,7 @@ handle_stream_data( handle_stream_data(Stream, Bin, _Flags, #{is_unidir := false} = State) -> %% for bidir stream, we just echo in place. ?tp(debug, #{stream => Stream, data => Bin, module => ?MODULE, dir => bidir}), - ct:pal("Server recv: ~p from ~p", [Bin, Stream]), + ?LOG("Server recv: ~p from ~p", [Bin, Stream]), case quicer:send(Stream, Bin) of {ok, _} -> {ok, State}; @@ -132,7 +133,7 @@ handle_stream_data( Stream, Bin, _Flags, #{is_unidir := true, peer_stream := PeerStream, conn := Conn} = State ) -> ?tp(debug, #{stream => Stream, data => Bin, module => ?MODULE, dir => unidir}), - ct:pal("Server recv: ~p from ~p", [Bin, Stream]), + ?LOG("Server recv: ~p from ~p", [Bin, Stream]), case PeerStream of undefined -> diff --git a/test/prop_quic_types.hrl b/test/prop_quic_types.hrl index 149c29f3..744429f2 100644 --- a/test/prop_quic_types.hrl +++ b/test/prop_quic_types.hrl @@ -129,7 +129,7 @@ | {mtu_discovery_search_complete_timeout_us, uint64()} | {mtu_discovery_missing_probe_count, uint8()} | {max_binding_stateless_operations, uint16()} - | {stateless_operation_expiration_ms, 10..(1 bsl 16 -1)}. + | {stateless_operation_expiration_ms, 10..(1 bsl 16 - 1)}. -type quicer_conn_opts() :: [conn_opt()]. -type conn_opt() :: diff --git a/test/quicer_connection_SUITE.erl b/test/quicer_connection_SUITE.erl index edb74f39..8dfa9c80 100644 --- a/test/quicer_connection_SUITE.erl +++ b/test/quicer_connection_SUITE.erl @@ -549,6 +549,7 @@ tc_datagram_disallowed(Config) -> %% THEN: It get an error ?assertEqual({error, dgram_send_error, invalid_state}, quicer:send_dgram(Conn, <<"dg_ping">>)), quicer:shutdown_connection(Conn), + ok = quicer:terminate_listener(mqtt), ok. tc_datagram_peer_allowed(Config) -> @@ -585,6 +586,7 @@ tc_datagram_peer_allowed(Config) -> ok end, quicer:shutdown_connection(Conn), + ok = quicer:terminate_listener(mqtt), ok. tc_datagram_local_peer_allowed(Config) -> diff --git a/test/quicer_ct.hrl b/test/quicer_ct.hrl new file mode 100644 index 00000000..036c54e9 --- /dev/null +++ b/test/quicer_ct.hrl @@ -0,0 +1,22 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2024 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- +-ifndef(QUICER_CT_HRL). +-define(QUICER_CT_HRL, true). + +-define(LOG(_Arg1, _Arg2), logger:debug(_Arg1, _Arg2)). + +%% QUICER_CT_HRL +-endif. From 3c5c6cab7d7ddcc9fa9f00738d95beddd4569946 Mon Sep 17 00:00:00 2001 From: William Yang Date: Wed, 11 Dec 2024 11:27:06 +0100 Subject: [PATCH 7/9] ci(cover): proper cover -noshrink --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 13007141..fd267ab7 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ proper: .PHONY: proper-cover proper-cover: mkdir -p coverage - QUICER_TEST_COVER=1 $(REBAR) as test proper -c -n 1000 + QUICER_TEST_COVER=1 $(REBAR) as test proper -c -n 1000 --noshrink lcov -c --directory c_build/CMakeFiles/quicer_nif.dir/c_src/ \ --exclude "${PWD}/msquic/src/inc/*" \ --output-file ./coverage/proper-lcov.info From 6971406b572ee6978d7a44dd58890f1adefd9b8a Mon Sep 17 00:00:00 2001 From: William Yang Date: Wed, 11 Dec 2024 17:38:08 +0100 Subject: [PATCH 8/9] tmp: find root casue of cover test stuck --- test/prop_quic_types.hrl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/prop_quic_types.hrl b/test/prop_quic_types.hrl index 744429f2..5284494e 100644 --- a/test/prop_quic_types.hrl +++ b/test/prop_quic_types.hrl @@ -106,7 +106,7 @@ | {stream_recv_window_default, 1..?UINT32_MAX} | {stream_recv_buffer_default, 4096..?UINT32_MAX} | {conn_flow_control_window, uint32()} - | {max_stateless_operations, 1..16} + %| {max_stateless_operations, 1..16} | {initial_window_packets, uint32()} | {send_idle_timeout_ms, uint32()} | {initial_rtt_ms, 1..?UINT32_MAX} From f21f79c32d3a9a84c979fa2b012ddb62ba8f3cca Mon Sep 17 00:00:00 2001 From: William Yang Date: Wed, 11 Dec 2024 18:41:45 +0100 Subject: [PATCH 9/9] test(proper): further limit testing range --- test/prop_quic_types.hrl | 2 +- test/quicer_prop_gen.erl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/prop_quic_types.hrl b/test/prop_quic_types.hrl index 5284494e..e7306f25 100644 --- a/test/prop_quic_types.hrl +++ b/test/prop_quic_types.hrl @@ -118,7 +118,7 @@ | {peer_unidi_stream_count, uint16()} | {retry_memory_limit, uint16()} | {load_balancing_mode, 0..2} - | {max_operations_per_drain, uint8()} + | {max_operations_per_drain, 1..(1 bsl 8 - 1)} | {send_buffering_enabled, 0 | 1} | {pacing_enabled, 0 | 1} | {migration_enabled, 0 | 1} diff --git a/test/quicer_prop_gen.erl b/test/quicer_prop_gen.erl index a1750d7e..dd0c3d24 100644 --- a/test/quicer_prop_gen.erl +++ b/test/quicer_prop_gen.erl @@ -301,8 +301,8 @@ valid_quicer_settings() -> quicer_setting_val_is_power_2(stream_recv_window_bidi_local_default, Opts) andalso quicer_setting_val_is_power_2(stream_recv_window_bidi_remote_default, Opts) andalso quicer_setting_val_is_power_2(stream_recv_window_unidi_default, Opts) andalso - (proplists:get_value(maximum_mtu, Opts, 1500) > - proplists:get_value(minimum_mtu, Opts, 1248)) + (proplists:get_value(maximum_mtu, lists:reverse(Opts), 1500) > + proplists:get_value(minimum_mtu, lists:reverse(Opts), 1248)) ). -spec ensure_dummy_listener(non_neg_integer()) -> _.