Skip to content

Commit ef73617

Browse files
authored
Merge pull request #62 from qzhuyan/dev/william/setopt4
refactor: setopt3 -> setopt4, more safe
2 parents 8f2f849 + 92f3c78 commit ef73617

File tree

8 files changed

+883
-46
lines changed

8 files changed

+883
-46
lines changed

c_src/quicer_config.c

Lines changed: 745 additions & 37 deletions
Large diffs are not rendered by default.

c_src/quicer_config.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ bool get_uint64_from_map(ErlNifEnv *env,
5656
uint64_t *value);
5757

5858
ERL_NIF_TERM getopt3(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
59-
ERL_NIF_TERM setopt3(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
59+
ERL_NIF_TERM setopt4(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
6060

6161
bool create_settings(ErlNifEnv *env,
6262
const ERL_NIF_TERM *emap,
@@ -65,4 +65,10 @@ bool create_settings(ErlNifEnv *env,
6565
bool
6666
parse_listen_on(ErlNifEnv *env, ERL_NIF_TERM elisten_on, QUIC_ADDR *Address);
6767

68+
ERL_NIF_TERM set_connection_opt(ErlNifEnv *env,
69+
QuicerConnCTX *c_ctx,
70+
ERL_NIF_TERM optname,
71+
ERL_NIF_TERM optval,
72+
ERL_NIF_TERM elevel);
73+
6874
#endif // __QUICER_CONFIG_H_

c_src/quicer_connection.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ ServerConnectionCallback(HQUIC Connection,
310310
acc = c_ctx->owner;
311311
acc_pid = &(acc->Pid);
312312

313+
// for fast connect:
313314
if (!(acc && enif_is_process_alive(c_ctx->env, acc_pid)))
314315
{
315316
acc = AcceptorDequeue(
@@ -323,6 +324,12 @@ ServerConnectionCallback(HQUIC Connection,
323324
}
324325

325326
assert(acc);
327+
328+
if (!acc->fast_conn)
329+
{
330+
enif_release_resource(c_ctx);
331+
}
332+
326333
// A monitor is automatically removed when it triggers or when the
327334
// resource is deallocated.
328335
enif_monitor_process(NULL, c_ctx, acc_pid, &c_ctx->owner_mon);
@@ -381,6 +388,12 @@ ServerConnectionCallback(HQUIC Connection,
381388
TP_CB_3(shutdown_complete,
382389
Connection,
383390
Event->SHUTDOWN_COMPLETE.AppCloseInProgress);
391+
392+
if (!c_ctx->owner->fast_conn
393+
&& !Event->SHUTDOWN_COMPLETE.HandshakeCompleted)
394+
{
395+
enif_release_resource(c_ctx);
396+
}
384397
report = enif_make_tuple3(
385398
env, ATOM_QUIC, ATOM_CLOSED, enif_make_resource(env, c_ctx));
386399

@@ -546,6 +559,22 @@ async_connect3(ErlNifEnv *env,
546559
}
547560
}
548561

562+
ERL_NIF_TERM evalue;
563+
if (enif_get_map_value(
564+
env, eoptions, ATOM_QUIC_PARAM_CONN_LOCAL_ADDRESS, &evalue))
565+
{
566+
if (!IS_SAME_TERM(ATOM_OK,
567+
set_connection_opt(env,
568+
c_ctx,
569+
ATOM_QUIC_PARAM_CONN_LOCAL_ADDRESS,
570+
evalue,
571+
ATOM_FALSE)))
572+
{
573+
destroy_c_ctx(c_ctx);
574+
return ERROR_TUPLE_2(ATOM_CONN_OPEN_ERROR);
575+
}
576+
}
577+
549578
if (QUIC_FAILED(Status = MsQuic->ConnectionStart(c_ctx->Connection,
550579
c_ctx->Configuration,
551580
QUIC_ADDRESS_FAMILY_UNSPEC,
@@ -743,9 +772,13 @@ continue_connection_handshake(QuicerConnCTX *c_ctx)
743772
return QUIC_STATUS_INTERNAL_ERROR;
744773
}
745774

775+
// and releases resource in callback
776+
enif_keep_resource(c_ctx);
777+
746778
if (QUIC_FAILED(Status = MsQuic->ConnectionSetConfiguration(
747779
c_ctx->Connection, c_ctx->l_ctx->Configuration)))
748780
{
781+
enif_release_resource(c_ctx);
749782
return Status;
750783
}
751784

@@ -756,6 +789,7 @@ continue_connection_handshake(QuicerConnCTX *c_ctx)
756789
sizeof(QUIC_SETTINGS),
757790
&c_ctx->owner->Settings)))
758791
{
792+
enif_release_resource(c_ctx);
759793
return Status;
760794
}
761795
return Status;

c_src/quicer_nif.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1000,7 +1000,7 @@ static ErlNifFunc nif_funcs[] = {
10001000
{ "async_close_stream", 3, close_stream3, 0},
10011001
{ "sockname", 1, sockname1, 0},
10021002
{ "getopt", 3, getopt3, 0},
1003-
{ "setopt", 3, setopt3, 0},
1003+
{ "setopt", 4, setopt4, 0},
10041004
{ "controlling_process", 2, controlling_process, 0},
10051005
/* for DEBUG */
10061006
{ "get_conn_rid", 1, get_conn_rid1, 1},

docs/todo.org

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* Features List
66
Feature todo list, priority descending
77
** add safety check for closed handlers
8+
check the is_closed flag or delay the close in ctx dealloc callback?
89

910
** Stream send iolist
1011

src/quicer.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ getopt(Handle, Opt, Optlevel) ->
411411
setopt(Handle, param_conn_settings, Value) when is_list(Value) ->
412412
setopt(Handle, param_conn_settings, maps:from_list(Value));
413413
setopt(Handle, Opt, Value) ->
414-
quicer_nif:setopt(Handle, Opt, Value).
414+
quicer_nif:setopt(Handle, Opt, Value, false).
415415

416416
-spec get_stream_id(Stream::stream_handler()) ->
417417
{ok, integer()} | {error, any()}.

src/quicer_nif.erl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
, async_close_stream/3
3232
, sockname/1
3333
, getopt/3
34-
, setopt/3
34+
, setopt/4
3535
, controlling_process/2
3636
]).
3737

@@ -164,11 +164,11 @@ sockname(_Conn) ->
164164
getopt(_Handle, _Optname, _IsRaw) ->
165165
erlang:nif_error(nif_library_not_loaded).
166166

167-
-spec setopt(handler(), optname(), any()) ->
167+
-spec setopt(handler(), optname(), any(), optlevel()) ->
168168
ok |
169169
{error, badarg | param_error | internal_error | not_enough_mem} |
170170
{error, atom_reason()}.
171-
setopt(_Handle, _Opt, _Value) ->
171+
setopt(_Handle, _Opt, _Value, _Level) ->
172172
erlang:nif_error(nif_library_not_loaded).
173173

174174
-spec get_conn_rid(connection_handler()) ->

test/quicer_SUITE.erl

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
, tc_conn_basic_slow_start/1
5050
, tc_conn_double_close/1
5151
, tc_conn_other_port/1
52+
, tc_conn_with_localaddr/1
5253
, tc_conn_controlling_process/1
5354

5455
, tc_stream_client_init/1
@@ -72,7 +73,11 @@
7273
, tc_getopt/1
7374
, tc_getopt_stream_active/1
7475
, tc_setopt/1
75-
, tc_setopt_conn_local_addr/1
76+
77+
%% @TODO following two tcs are failing due to:
78+
% https://github.com/microsoft/msquic/issues/2033
79+
% , tc_setopt_conn_local_addr/1
80+
% , tc_setopt_conn_local_addr_in_use/1
7681
, tc_strm_opt_active_n/1
7782
, tc_strm_opt_active_once/1
7883
, tc_strm_opt_active_1/1
@@ -369,6 +374,26 @@ tc_conn_other_port(Config)->
369374
ct:fail("timeout")
370375
end.
371376

377+
tc_conn_with_localaddr(Config)->
378+
Port = 5568,
379+
Owner = self(),
380+
{SPid, Ref} = spawn_monitor(fun() -> simple_conn_server(Owner, Config, Port) end),
381+
382+
{ok, CPort0} = gen_udp:open(0, [{ip, {127, 0, 0, 1}}]),
383+
{ok, {{127, 0, 0, 1}, PortX}} = inet:sockname(CPort0),
384+
ok = gen_udp:close(CPort0),
385+
receive
386+
listener_ready ->
387+
{ok, Conn} = quicer:connect("127.0.0.1", Port, [{param_conn_local_address, "127.0.0.1:" ++ integer_to_list(PortX)}
388+
| default_conn_opts()], 5000),
389+
?assertEqual({ok, {{127,0,0,1}, PortX}}, quicer:sockname(Conn)),
390+
ok = quicer:close_connection(Conn),
391+
SPid ! done,
392+
ok = ensure_server_exit_normal(Ref)
393+
after 1000 ->
394+
ct:fail("timeout")
395+
end.
396+
372397
tc_stream_client_init(Config) ->
373398
Port = 4568,
374399
Owner = self(),
@@ -862,6 +887,7 @@ tc_getstat_closed(Config) ->
862887
{ok, 4} = quicer:send(Stm, <<"ping">>),
863888
receive {quic, _, _, _,_,_} -> ok end,
864889
ok = quicer:close_stream(Stm),
890+
ok = quicer:close_connection(Conn),
865891
case quicer:getstat(Conn, [send_cnt, recv_oct, send_pend]) of
866892
{error,invalid_parameter} -> ok;
867893
{error,invalid_state} -> ok;
@@ -1045,8 +1071,13 @@ tc_setopt_conn_local_addr(Config) ->
10451071
%% change local addr with a new port 5060
10461072
?assertEqual(ok, quicer:setopt(Conn, param_conn_local_address, "127.0.0.1:50600")),
10471073
%% sleep is needed to finish migration at protocol level
1048-
timer:sleep(200),
1049-
?assertEqual({ok, {{127,0,0,1}, 50600}}, quicer:sockname(Stm0)),
1074+
retry_with(fun() ->
1075+
timer:sleep(100),
1076+
case quicer:sockname(Stm0) of
1077+
{ok, {{127,0,0,1}, 50600}} -> true;
1078+
{ok, Other} -> {false, Other}
1079+
end
1080+
end, 20, "addr migration failed"),
10501081
{ok, 5} = quicer:send(Stm0, <<"ping2">>),
10511082
receive
10521083
{quic, <<"ping2">>, Stm0, _, _, _} ->
@@ -1061,6 +1092,52 @@ tc_setopt_conn_local_addr(Config) ->
10611092
SPid ! done,
10621093
ensure_server_exit_normal(Ref).
10631094

1095+
tc_setopt_conn_local_addr_in_use(Config) ->
1096+
Port = 4578,
1097+
Owner = self(),
1098+
{SPid, Ref} = spawn_monitor(fun() -> echo_server(Owner, Config, Port) end),
1099+
{ok, Conn} = quicer:connect("127.0.0.1", Port, default_conn_opts(), 5000),
1100+
{ok, Stm0} = quicer:start_stream(Conn, [{active, true}]),
1101+
{ok, 5} = quicer:send(Stm0, <<"ping1">>),
1102+
receive
1103+
{quic, <<"ping1">>, Stm0, _, _, _} ->
1104+
ok
1105+
after 1000 ->
1106+
ct:fail("recv ping1 timeout")
1107+
end,
1108+
{ok, OldAddr} = quicer:sockname(Stm0),
1109+
%% change local addr with a new random port (0)
1110+
?assertEqual(ok, quicer:setopt(Conn, param_conn_local_address, "127.0.0.1:0")),
1111+
%% sleep is needed to finish migration at protocol level
1112+
timer:sleep(50),
1113+
{ok, NewAddr} = quicer:sockname(Stm0),
1114+
?assertNotEqual(OldAddr, NewAddr),
1115+
?assertNotEqual({ok, {{127,0,0,1}, 50600}}, NewAddr),
1116+
?assertNotEqual({ok, {{127,0,0,1}, 50600}}, OldAddr),
1117+
1118+
%% Occupy 50600
1119+
{ok, ESocket} = gen_udp:open(50600, [{ip, element(1, NewAddr)}]),
1120+
%% change local addr with a new port 5060
1121+
?assertEqual({error,address_in_use}, quicer:setopt(Conn, param_conn_local_address, "127.0.0.1:50600")),
1122+
1123+
%gen_udp:close(ESocket),
1124+
1125+
%% sleep is needed to finish migration at protocol level
1126+
ct:pal("send after migration failed"),
1127+
{ok, 5} = quicer:send(Stm0, <<"ping2">>),
1128+
receive
1129+
{quic, <<"ping2">>, Stm0, _, _, _} ->
1130+
ok
1131+
after 1000 ->
1132+
ct:fail("recv ping2 timeout")
1133+
end,
1134+
%% check with server if peer addr is correct.
1135+
SPid ! {peer_addr, self()},
1136+
receive {peer_addr, Peer} -> ok end,
1137+
?assertEqual({ok, NewAddr}, Peer),
1138+
SPid ! done,
1139+
ensure_server_exit_normal(Ref).
1140+
10641141
tc_app_echo_server(Config) ->
10651142
Port = 8888,
10661143
application:ensure_all_started(quicer),
@@ -1451,6 +1528,17 @@ active_recv(Stream, Len, BinList) ->
14511528
end
14521529
end.
14531530

1531+
retry_with(_Fun, 0, ErrorInfo) ->
1532+
ct:fail(ErrorInfo);
1533+
retry_with(Fun, Retry, ErrorInfo) ->
1534+
case Fun() of
1535+
true ->
1536+
ok;
1537+
false ->
1538+
retry_with(Fun, Retry - 1, ErrorInfo);
1539+
{false, NewErrorInfo} ->
1540+
retry_with(Fun, Retry - 1, NewErrorInfo)
1541+
end.
14541542
%%%_* Emacs ====================================================================
14551543
%%% Local Variables:
14561544
%%% allout-layout: t

0 commit comments

Comments
 (0)