chore: use gen_tcp:recv timeout to wait socket closed
This commit is contained in:
parent
4307aecefd
commit
660a1ce99d
|
@ -59,7 +59,7 @@
|
||||||
-export([format_bind/1]).
|
-export([format_bind/1]).
|
||||||
|
|
||||||
-ifdef(TEST).
|
-ifdef(TEST).
|
||||||
-export([certs_dir/2]).
|
-export([certs_dir/2, wait_listener_stopped/1]).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
-export_type([listener_id/0]).
|
-export_type([listener_id/0]).
|
||||||
|
@ -355,21 +355,33 @@ do_stop_listener(Type, Id, #{bind := ListenOn}) when ?ESOCKD_LISTENER(Type) ->
|
||||||
do_stop_listener(Type, Id, #{bind := ListenOn}) when ?COWBOY_LISTENER(Type) ->
|
do_stop_listener(Type, Id, #{bind := ListenOn}) when ?COWBOY_LISTENER(Type) ->
|
||||||
case cowboy:stop_listener(Id) of
|
case cowboy:stop_listener(Id) of
|
||||||
ok ->
|
ok ->
|
||||||
wait_listener_stopped(ListenOn, 0);
|
_ = wait_listener_stopped(ListenOn),
|
||||||
|
ok;
|
||||||
Error ->
|
Error ->
|
||||||
Error
|
Error
|
||||||
end;
|
end;
|
||||||
do_stop_listener(quic, Id, _Conf) ->
|
do_stop_listener(quic, Id, _Conf) ->
|
||||||
quicer:terminate_listener(Id).
|
quicer:terminate_listener(Id).
|
||||||
|
|
||||||
wait_listener_stopped(ListenOn, RetryMs) ->
|
wait_listener_stopped(ListenOn) ->
|
||||||
|
wait_listener_stopped(ListenOn, 0).
|
||||||
|
|
||||||
|
wait_listener_stopped(ListenOn, 3) ->
|
||||||
|
Log = #{
|
||||||
|
msg => "cowboy_listener_not_closed_when_stopping_listener",
|
||||||
|
listener => ListenOn,
|
||||||
|
wait_seconds => 9
|
||||||
|
},
|
||||||
|
?SLOG(warning, Log),
|
||||||
|
timeout;
|
||||||
|
wait_listener_stopped(ListenOn, RetryCount) ->
|
||||||
% NOTE
|
% NOTE
|
||||||
% `cowboy:stop_listener/1` will not close the listening socket explicitly,
|
% `cowboy:stop_listener/1` will not close the listening socket explicitly,
|
||||||
% it will be closed by the runtime system **only after** the process exits.
|
% it will be closed by the runtime system **only after** the process exits.
|
||||||
Endpoint = maps:from_list(ip_port(ListenOn)),
|
Endpoint = maps:from_list(ip_port(ListenOn)),
|
||||||
case
|
case
|
||||||
gen_tcp:connect(
|
gen_tcp:connect(
|
||||||
maps:get(ip, Endpoint, loopback),
|
maps:get(ip, Endpoint, "127.0.0.1"),
|
||||||
maps:get(port, Endpoint),
|
maps:get(port, Endpoint),
|
||||||
[{active, false}]
|
[{active, false}]
|
||||||
)
|
)
|
||||||
|
@ -380,26 +392,18 @@ wait_listener_stopped(ListenOn, RetryMs) ->
|
||||||
%% but don't want to crash if not, because this doesn't make any difference.
|
%% but don't want to crash if not, because this doesn't make any difference.
|
||||||
ok;
|
ok;
|
||||||
{ok, Socket} ->
|
{ok, Socket} ->
|
||||||
%% NOTE
|
%% cowboy(ws/wss) will close the socket if we don't send packet in 5 seconds.
|
||||||
%% Tiny chance to get a connected socket here, when some other process
|
%% so we only wait 3 second here.
|
||||||
%% concurrently binds to the same port.
|
case gen_tcp:recv(Socket, 0, 3000) of
|
||||||
gen_tcp:close(Socket),
|
{ok, _} ->
|
||||||
Log = #{
|
_ = gen_tcp:close(Socket),
|
||||||
msg => "cowboy_listener_still_open_after_stopping_listener",
|
wait_listener_stopped(ListenOn, RetryCount + 1);
|
||||||
listener => ListenOn,
|
{error, timeout} ->
|
||||||
wait_ms => RetryMs
|
_ = gen_tcp:close(Socket),
|
||||||
},
|
wait_listener_stopped(ListenOn, RetryCount + 1);
|
||||||
case RetryMs >= 3000 of
|
{error, _} ->
|
||||||
true ->
|
_ = gen_tcp:close(Socket),
|
||||||
%% Don't stop this listener, in case other processes start it again.
|
ok
|
||||||
?SLOG(warning, Log),
|
|
||||||
ok;
|
|
||||||
false ->
|
|
||||||
?SLOG(info, Log),
|
|
||||||
Interval = 300,
|
|
||||||
NewRetryMs = RetryMs + Interval,
|
|
||||||
timer:sleep(Interval),
|
|
||||||
wait_listener_stopped(ListenOn, NewRetryMs)
|
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ end_per_suite(Config) ->
|
||||||
init_per_testcase(Case, Config) when
|
init_per_testcase(Case, Config) when
|
||||||
Case =:= t_start_stop_listeners;
|
Case =:= t_start_stop_listeners;
|
||||||
Case =:= t_restart_listeners;
|
Case =:= t_restart_listeners;
|
||||||
|
Case =:= t_wait_for_stop_listeners;
|
||||||
Case =:= t_restart_listeners_with_hibernate_after_disabled
|
Case =:= t_restart_listeners_with_hibernate_after_disabled
|
||||||
->
|
->
|
||||||
ok = emqx_listeners:stop(),
|
ok = emqx_listeners:stop(),
|
||||||
|
@ -57,6 +58,36 @@ t_start_stop_listeners(_) ->
|
||||||
?assertException(error, _, emqx_listeners:start_listener(ws, {"127.0.0.1", 8083}, #{})),
|
?assertException(error, _, emqx_listeners:start_listener(ws, {"127.0.0.1", 8083}, #{})),
|
||||||
ok = emqx_listeners:stop().
|
ok = emqx_listeners:stop().
|
||||||
|
|
||||||
|
t_wait_for_stop_listeners(_) ->
|
||||||
|
ok = emqx_listeners:start(),
|
||||||
|
meck:new([cowboy], [passthrough, no_history, no_link]),
|
||||||
|
%% mock stop_listener return ok but listen port is still open
|
||||||
|
meck:expect(cowboy, stop_listener, fun(_) -> ok end),
|
||||||
|
List = [
|
||||||
|
{<<"ws:default">>, {"127.0.0.1", 8083}},
|
||||||
|
{<<"wss:default">>, {"127.0.0.1", 8084}}
|
||||||
|
],
|
||||||
|
lists:foreach(
|
||||||
|
fun({Id, ListenerOn}) ->
|
||||||
|
Start = erlang:system_time(seconds),
|
||||||
|
ok = emqx_listeners:stop_listener(Id),
|
||||||
|
?assertEqual(timeout, emqx_listeners:wait_listener_stopped(ListenerOn)),
|
||||||
|
End = erlang:system_time(seconds),
|
||||||
|
?assert(End - Start >= 9, "wait_listener_stopped should wait at least 9 seconds")
|
||||||
|
end,
|
||||||
|
List
|
||||||
|
),
|
||||||
|
meck:unload(cowboy),
|
||||||
|
lists:foreach(
|
||||||
|
fun({Id, ListenerOn}) ->
|
||||||
|
ok = emqx_listeners:stop_listener(Id),
|
||||||
|
?assertEqual(ok, emqx_listeners:wait_listener_stopped(ListenerOn))
|
||||||
|
end,
|
||||||
|
List
|
||||||
|
),
|
||||||
|
ok = emqx_listeners:stop(),
|
||||||
|
ok.
|
||||||
|
|
||||||
t_restart_listeners(_) ->
|
t_restart_listeners(_) ->
|
||||||
ok = emqx_listeners:start(),
|
ok = emqx_listeners:start(),
|
||||||
ok = emqx_listeners:stop(),
|
ok = emqx_listeners:stop(),
|
||||||
|
|
Loading…
Reference in New Issue