diff --git a/test/quicer_SUITE.erl b/test/quicer_SUITE.erl index e8809a9f..fd8ea6e8 100644 --- a/test/quicer_SUITE.erl +++ b/test/quicer_SUITE.erl @@ -44,15 +44,6 @@ , tc_lib_registration_neg/1 , tc_open_listener_inval_reg/1 - - , tc_conn_controlling_process/1 - , tc_conn_controlling_process_demon/1 - - , tc_conn_opt_ideal_processor/1 - , tc_conn_opt_share_udp_binding/1 - , tc_conn_opt_local_uni_stream_count/1 - , tc_conn_opt_local_bidi_stream_count/1 - , tc_stream_client_init/1 , tc_stream_client_send_binary/1 , tc_stream_client_send_iolist/1 @@ -320,73 +311,6 @@ tc_open_listener_inval_reg(Config) -> quicer:reg_open(), ok. -tc_conn_opt_ideal_processor(Config) -> - Port = select_port(), - Owner = self(), - {_SPid, _Ref} = spawn_monitor(fun() -> echo_server(Owner, Config, Port) end), - receive - listener_ready -> - {ok, Conn} = quicer:connect("127.0.0.1", Port, default_conn_opts(), 5000), - {ok, Stm} = quicer:start_stream(Conn, []), - {ok, 4} = quicer:send(Stm, <<"ping">>), - {ok, Processor} = quicer:getopt(Conn, param_conn_ideal_processor), - ?assert(is_integer(Processor)), - ok = quicer:close_connection(Conn) - after 5000 -> - ct:fail("listener_timeout") - end. - -tc_conn_opt_share_udp_binding(Config) -> - Port = select_port(), - Owner = self(), - {_SPid, _Ref} = spawn_monitor(fun() -> echo_server(Owner, Config, Port) end), - receive - listener_ready -> - {ok, Conn} = quicer:connect("127.0.0.1", Port, default_conn_opts(), 5000), - {ok, Stm} = quicer:start_stream(Conn, []), - {ok, 4} = quicer:send(Stm, <<"ping">>), - {ok, IsShared} = quicer:getopt(Conn, param_conn_share_udp_binding), - ?assert(is_boolean(IsShared)), - {error, invalid_state} = quicer:setopt(Conn, param_conn_share_udp_binding, not IsShared), - {ok, IsShared} = quicer:getopt(Conn, param_conn_share_udp_binding), - ok = quicer:close_connection(Conn) - after 5000 -> - ct:fail("listener_timeout") - end. - -tc_conn_opt_local_bidi_stream_count(Config) -> - Port = select_port(), - Owner = self(), - {_SPid, _Ref} = spawn_monitor(fun() -> echo_server(Owner, Config, Port) end), - receive - listener_ready -> - {ok, Conn} = quicer:connect("127.0.0.1", Port, default_conn_opts(), 5000), - {ok, Stm} = quicer:start_stream(Conn, []), - {ok, 4} = quicer:send(Stm, <<"ping">>), - {ok, Cnt} = quicer:getopt(Conn, param_conn_local_bidi_stream_count), - ?assert(is_integer(Cnt)), - {error, invalid_parameter} = quicer:setopt(Conn, param_conn_local_bidi_stream_count, Cnt + 2), - ok = quicer:close_connection(Conn) - after 5000 -> - ct:fail("listener_timeout") - end. - -tc_conn_opt_local_uni_stream_count(Config) -> - Port = select_port(), - Owner = self(), - {_SPid, _Ref} = spawn_monitor(fun() -> echo_server(Owner, Config, Port) end), - receive - listener_ready -> - {ok, Conn} = quicer:connect("127.0.0.1", Port, default_conn_opts(), 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)), - {error, invalid_parameter} = quicer:setopt(Conn, param_conn_local_unidi_stream_count, Cnt + 2), - ok = quicer:close_connection(Conn) - after 5000 -> - ct:fail("listener_timeout") - end. tc_stream_client_init(Config) -> Port = select_port(), @@ -894,79 +818,7 @@ dgram_client_recv_loop(Conn, ReceivedOnStream, ReceivedViaDgram) -> ct:fail("Unexpected Msg ~p", [Other]) end. -tc_conn_controlling_process(Config) -> - Port = select_port(), - Owner = self(), - {SPid, Ref} = spawn_monitor(fun() -> echo_server(Owner, Config, Port) end), - receive - listener_ready -> - {ok, Conn} = quicer:connect("localhost", Port, default_conn_opts(), 5000), - {ok, Stm} = quicer:start_stream(Conn, [{active, false}]), - ok = quicer:controlling_process(Conn, self()), - {ok, 11} = quicer:send(Stm, <<"ping_active">>), - {ok, _} = quicer:recv(Stm, 11), - {NewOwner, MonRef} = spawn_monitor( - fun() -> - receive - {quic, closed, Conn, _Flags} -> - ok - end - end), - ok = quicer:controlling_process(Conn, NewOwner), - %% Trigger *async* connection shutdown since I am not the conn owner - quicer:async_shutdown_connection(Conn, ?QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, 0), - receive - {'DOWN', MonRef, process, NewOwner, normal} -> - SPid ! done - end, - ensure_server_exit_normal(Ref) - after 6000 -> - ct:fail("timeout") - end. - -%% @doc check old owner is demonitored. -tc_conn_controlling_process_demon(Config) -> - Port = select_port(), - Owner = self(), - {SPid, Ref} = spawn_monitor(fun() -> echo_server(Owner, Config, Port) end), - receive - listener_ready -> - Parent = self(), - {OldOwner, MonRef} = spawn_monitor( - fun() -> - {ok, Conn} = quicer:connect("localhost", Port, default_conn_opts(), 5000), - Res = quicer:controlling_process(Conn, Parent), - exit({Res, Conn}) - end), - Conn = receive - {'DOWN', MonRef, process, OldOwner, {Res, TheConn}} -> - ct:pal("Old Owner is down, mon res: ~p", [Res]), - TheConn - end, - %% Try set owner back to dead previous owner, should fail - ?assertEqual({error, owner_dead}, quicer:controlling_process(Conn, OldOwner)), - %% rollback to this owner. - {ok, Stm} = quicer:start_stream(Conn, [{active, false}]), - {ok, 11} = quicer:send(Stm, <<"ping_active">>), - {ok, _} = quicer:recv(Stm, 11), - SPid ! done, - - {NewOwner2, MonRef2} = spawn_monitor(fun() -> - receive stop -> ok end - end), - ok = quicer:controlling_process(Conn, NewOwner2), - NewOwner2 ! stop, - receive - {'DOWN', MonRef2, process, NewOwner2, normal} -> ok - end, - ?assertNotMatch({ok, _}, quicer:send(Stm, <<"ping_active">>)), - quicer:async_shutdown_connection(Conn, ?QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, 0), - SPid ! done, - ensure_server_exit_normal(Ref) - after 6000 -> - ct:fail("timeout") - end. %% @doc test conn and stream share the same owner. tc_conn_and_stream_shared_owner(Config) -> diff --git a/test/quicer_connection_SUITE.erl b/test/quicer_connection_SUITE.erl index b2b50456..3530e638 100644 --- a/test/quicer_connection_SUITE.erl +++ b/test/quicer_connection_SUITE.erl @@ -18,6 +18,8 @@ -compile(export_all). -compile(nowarn_export_all). +-include("quicer.hrl"). + -include_lib("common_test/include/ct.hrl"). -include_lib("stdlib/include/assert.hrl"). -include_lib("snabbkaffe/include/snabbkaffe.hrl"). @@ -479,6 +481,148 @@ run_tc_conn_client_bad_cert(Config)-> ct:fail("timeout") end. +%% @doc check old owner is demonitored. +tc_conn_controlling_process_demon(Config) -> + Port = select_port(), + Owner = self(), + {SPid, Ref} = spawn_monitor(fun() -> echo_server(Owner, Config, Port) end), + receive + listener_ready -> + Parent = self(), + {OldOwner, MonRef} = spawn_monitor( + fun() -> + {ok, Conn} = quicer:connect("localhost", Port, default_conn_opts(), 5000), + Res = quicer:controlling_process(Conn, Parent), + exit({Res, Conn}) + end), + Conn = receive + {'DOWN', MonRef, process, OldOwner, {Res, TheConn}} -> + ct:pal("Old Owner is down, mon res: ~p", [Res]), + TheConn + end, + %% Try set owner back to dead previous owner, should fail + ?assertEqual({error, owner_dead}, quicer:controlling_process(Conn, OldOwner)), + %% rollback to this owner. + + {ok, Stm} = quicer:start_stream(Conn, [{active, false}]), + {ok, 11} = quicer:send(Stm, <<"ping_active">>), + {ok, _} = quicer:recv(Stm, 11), + SPid ! done, + + {NewOwner2, MonRef2} = spawn_monitor(fun() -> + receive stop -> ok end + end), + ok = quicer:controlling_process(Conn, NewOwner2), + NewOwner2 ! stop, + receive + {'DOWN', MonRef2, process, NewOwner2, normal} -> ok + end, + ?assertNotMatch({ok, _}, quicer:send(Stm, <<"ping_active">>)), + quicer:async_shutdown_connection(Conn, ?QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, 0), + SPid ! done, + ensure_server_exit_normal(Ref) + after 6000 -> + ct:fail("timeout") + end. + +tc_conn_controlling_process(Config) -> + Port = select_port(), + Owner = self(), + {SPid, Ref} = spawn_monitor(fun() -> echo_server(Owner, Config, Port) end), + receive + listener_ready -> + {ok, Conn} = quicer:connect("localhost", Port, default_conn_opts(), 5000), + {ok, Stm} = quicer:start_stream(Conn, [{active, false}]), + ok = quicer:controlling_process(Conn, self()), + {ok, 11} = quicer:send(Stm, <<"ping_active">>), + {ok, _} = quicer:recv(Stm, 11), + {NewOwner, MonRef} = spawn_monitor( + fun() -> + receive + {quic, closed, Conn, _Flags} -> + ok + end + end), + ok = quicer:controlling_process(Conn, NewOwner), + %% Trigger *async* connection shutdown since I am not the conn owner + quicer:async_shutdown_connection(Conn, ?QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, 0), + receive + {'DOWN', MonRef, process, NewOwner, normal} -> + SPid ! done + end, + ensure_server_exit_normal(Ref) + after 6000 -> + ct:fail("timeout") + end. + +tc_conn_opt_ideal_processor(Config) -> + Port = select_port(), + Owner = self(), + {_SPid, _Ref} = spawn_monitor(fun() -> echo_server(Owner, Config, Port) end), + receive + listener_ready -> + {ok, Conn} = quicer:connect("127.0.0.1", Port, default_conn_opts(), 5000), + {ok, Stm} = quicer:start_stream(Conn, []), + {ok, 4} = quicer:send(Stm, <<"ping">>), + {ok, Processor} = quicer:getopt(Conn, param_conn_ideal_processor), + ?assert(is_integer(Processor)), + ok = quicer:close_connection(Conn) + after 5000 -> + ct:fail("listener_timeout") + end. + +tc_conn_opt_share_udp_binding(Config) -> + Port = select_port(), + Owner = self(), + {_SPid, _Ref} = spawn_monitor(fun() -> echo_server(Owner, Config, Port) end), + receive + listener_ready -> + {ok, Conn} = quicer:connect("127.0.0.1", Port, default_conn_opts(), 5000), + {ok, Stm} = quicer:start_stream(Conn, []), + {ok, 4} = quicer:send(Stm, <<"ping">>), + {ok, IsShared} = quicer:getopt(Conn, param_conn_share_udp_binding), + ?assert(is_boolean(IsShared)), + {error, invalid_state} = quicer:setopt(Conn, param_conn_share_udp_binding, not IsShared), + {ok, IsShared} = quicer:getopt(Conn, param_conn_share_udp_binding), + ok = quicer:close_connection(Conn) + after 5000 -> + ct:fail("listener_timeout") + end. + +tc_conn_opt_local_bidi_stream_count(Config) -> + Port = select_port(), + Owner = self(), + {_SPid, _Ref} = spawn_monitor(fun() -> echo_server(Owner, Config, Port) end), + receive + listener_ready -> + {ok, Conn} = quicer:connect("127.0.0.1", Port, default_conn_opts(), 5000), + {ok, Stm} = quicer:start_stream(Conn, []), + {ok, 4} = quicer:send(Stm, <<"ping">>), + {ok, Cnt} = quicer:getopt(Conn, param_conn_local_bidi_stream_count), + ?assert(is_integer(Cnt)), + {error, invalid_parameter} = quicer:setopt(Conn, param_conn_local_bidi_stream_count, Cnt + 2), + ok = quicer:close_connection(Conn) + after 5000 -> + ct:fail("listener_timeout") + end. + +tc_conn_opt_local_uni_stream_count(Config) -> + Port = select_port(), + Owner = self(), + {_SPid, _Ref} = spawn_monitor(fun() -> echo_server(Owner, Config, Port) end), + receive + listener_ready -> + {ok, Conn} = quicer:connect("127.0.0.1", Port, default_conn_opts(), 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)), + {error, invalid_parameter} = quicer:setopt(Conn, param_conn_local_unidi_stream_count, Cnt + 2), + ok = quicer:close_connection(Conn) + after 5000 -> + ct:fail("listener_timeout") + end. + %%% %%% Helpers @@ -607,3 +751,109 @@ default_listen_opts_client_cert(Config) -> [ {cacertfile, filename:join(DataDir, "ca.pem")} , {verify, peer} | tl(default_listen_opts(Config)) ]. + + +echo_server(Owner, Config, Port)-> + put(echo_server_test_coordinator, Owner), + case quicer:listen(Port, default_listen_opts(Config)) of + {ok, L} -> + Owner ! listener_ready, + {ok, Conn} = quicer:accept(L, [], 5000), + {ok, Conn} = quicer:async_accept_stream(Conn, []), + {ok, Conn} = quicer:handshake(Conn), + ct:pal("echo server conn accepted", []), + receive + {quic, new_stream, Stm, _Props} -> + {ok, Conn} = quicer:async_accept_stream(Conn, []); + {flow_ctl, BidirCount, UniDirCount} -> + ct:pal("echo server stream flow control to bidirectional: ~p : ~p", [BidirCount, UniDirCount]), + quicer:setopt(Conn, param_conn_settings, #{peer_bidi_stream_count => BidirCount, + peer_unidi_stream_count => UniDirCount}), + receive {quic, new_stream, Stm, _Props} -> + {ok, Conn} = quicer:async_accept_stream(Conn, []) + end + end, + ct:pal("echo server stream accepted", []), + echo_server_stm_loop(L, Conn, [Stm]); + {error, listener_start_error, 200000002} -> + ct:pal("echo_server: listener_start_error", []), + timer:sleep(100), + echo_server(Owner, Config, Port) + end. + +echo_server_stm_loop(L, Conn, Stms) -> + receive + {quic, <<"Abort">>, Stm, #{flags := _Flag}} -> + quicer:async_shutdown_stream(Stm, ?QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 1), + echo_server_stm_loop(L, Conn, Stms); + {quic, Bin, Stm, #{flags := Flag}} when is_binary(Bin) -> + SendFlag = case (Flag band ?QUIC_RECEIVE_FLAG_FIN) > 0 of + true -> ?QUICER_SEND_FLAG_SYNC bor ?QUIC_SEND_FLAG_FIN; + false -> ?QUICER_SEND_FLAG_SYNC + end, + case quicer:send(Stm, Bin, SendFlag) of + {error, stm_send_error, aborted} -> + ct:pal("echo server: send aborted: ~p ", [Bin]); + {error, stm_send_error, invalid_state} -> + {ok, RetStream} = + quicer:start_stream(Conn, [{open_flag, ?QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL}]), + quicer:send(RetStream, Bin); + {error, cancelled} -> + ct:pal("echo server: send cancelled: ~p ", [Bin]), + cancelled; + {ok, _} -> + ok + end, + echo_server_stm_loop(L, Conn, Stms); + {quic, peer_send_aborted, Stm, _Error} -> + ct:pal("echo server peer_send_aborted", []), + quicer:close_stream(Stm), + echo_server_stm_loop(L, Conn, Stms); + {quic, peer_send_shutdown, Stm, undefined} -> + ct:pal("echo server peer_send_shutdown", []), + quicer:close_stream(Stm), + echo_server_stm_loop(L, Conn, Stms); + {quic, transport_shutdown, Conn, #{status := ErrorAtom}} -> + ct:pal("echo server transport_shutdown due to ~p", [ErrorAtom]), + get(echo_server_test_coordinator) ! {echo_server_transport_shutdown, ErrorAtom}, + echo_server_stm_loop(L, Conn, Stms); + {quic, shutdown, Conn, ErrorCode} -> + ct:pal("echo server conn shutdown ~p due to ~p", [Conn, ErrorCode]), + quicer:close_connection(Conn), + echo_server_stm_loop(L, Conn, Stms); + {quic, closed, Conn, _Flags} -> + ct:pal("echo server Conn closed", []), + echo_server_stm_loop(L, Conn, Stms); + {quic, stream_closed, Stm, Flag} -> + ct:pal("echo server stream closed ~p", [Flag]), + echo_server_stm_loop(L, Conn, Stms -- [Stm]); + {set_stm_cnt, N } -> + ct:pal("echo_server: set max stream count: ~p", [N]), + ok = quicer:setopt(Conn, param_conn_settings, #{peer_bidi_stream_count => N}), + {ok, NewStm} = quicer:accept_stream(Conn, []), + echo_server_stm_loop(L, Conn, [NewStm | Stms]); + {peer_addr, From} -> + From ! {peer_addr, quicer:peername(Conn)}, + echo_server_stm_loop(L, Conn, Stms); + {flow_ctl, BidirCount, UniDirCount} -> + ct:pal("echo server stream flow control to bidirectional: ~p : ~p", [BidirCount, UniDirCount]), + quicer:setopt(Conn, param_conn_settings, #{peer_bidi_stream_count => BidirCount, + peer_unidi_stream_count => UniDirCount}), + {ok, Conn} = quicer:async_accept_stream(Conn, []), + echo_server_stm_loop(L, Conn, Stms); + {quic, new_stream, NewStm, #{flags := Flags}} -> + NewStmList = case quicer:is_unidirectional(Flags) of + true -> + ct:pal("echo server: new incoming unidirectional stream"), + {ok, ReturnStm} = quicer:start_stream(Conn, [{open_flag, ?QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL}]), + [{NewStm, ReturnStm} | Stms]; + false -> + ct:pal("echo server: new incoming binary stream"), + [NewStm | Stms] + end, + echo_server_stm_loop(L, Conn, NewStmList); + done -> + ct:pal("echo server shutting down", []), + quicer:async_close_connection(Conn), + quicer:close_listener(L) + end.