From 3303dac8ba3415569ca42bdc2eeccc6d5ee2c845 Mon Sep 17 00:00:00 2001 From: Maria Scott Date: Fri, 3 Sep 2021 14:03:08 +0200 Subject: Enable usage of experimental inet_backend option for TCP listeners --- Makefile | 2 +- src/ranch_acceptors_sup.erl | 7 +- src/ranch_tcp.erl | 20 +++-- test/acceptor_SUITE.erl | 175 ++++++++++++++++++++++++++++++++------------ test/ranch_ct_hook.erl | 2 + test/sendfile_SUITE.erl | 27 ++++++- test/stampede_SUITE.erl | 77 ++++++++++++++++--- 7 files changed, 236 insertions(+), 74 deletions(-) diff --git a/Makefile b/Makefile index 9376231..48aa1f5 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ DOC_DEPS = asciideck TEST_DEPS = $(if $(CI_ERLANG_MK),ci.erlang.mk) ct_helper stampede dep_ct_helper = git https://github.com/ninenines/ct_helper master -dep_stampede = git https://github.com/juhlig/stampede 0.5.0 +dep_stampede = git https://github.com/juhlig/stampede 0.6.0 # Concuerror tests. diff --git a/src/ranch_acceptors_sup.erl b/src/ranch_acceptors_sup.erl index 76155b8..7e43179 100644 --- a/src/ranch_acceptors_sup.erl +++ b/src/ranch_acceptors_sup.erl @@ -60,12 +60,7 @@ start_listen_sockets(Ref, NumListenSockets, Transport, TransOpts0, Logger) when []; {_, Port} -> SocketOpts = maps:get(socket_opts, TransOpts0, []), - SocketOpts1 = case lists:keyfind(port, 1, SocketOpts) of - {port, Port} -> - SocketOpts; - _ -> - [{port, Port}|lists:keydelete(port, 1, SocketOpts)] - end, + SocketOpts1 = lists:keystore(port, 1, SocketOpts, {port, Port}), TransOpts1 = TransOpts0#{socket_opts => SocketOpts1}, [{N, start_listen_socket(Ref, Transport, TransOpts1, Logger)} || N <- lists:seq(2, NumListenSockets)] diff --git a/src/ranch_tcp.erl b/src/ranch_tcp.erl index 41f59c9..060f1ca 100644 --- a/src/ranch_tcp.erl +++ b/src/ranch_tcp.erl @@ -90,16 +90,24 @@ messages() -> {tcp, tcp_closed, tcp_error, tcp_passive}. listen(TransOpts) -> ok = cleanup(TransOpts), Logger = maps:get(logger, TransOpts, logger), - SocketOpts0 = maps:get(socket_opts, TransOpts, []), + SocketOpts = maps:get(socket_opts, TransOpts, []), + %% We set the port to 0 because it is given in the Opts directly. + %% The port in the options takes precedence over the one in the + %% first argument. + gen_tcp:listen(0, prepare_socket_opts(SocketOpts, Logger)). + +prepare_socket_opts([Backend = {inet_backend, _}|SocketOpts], Logger) -> + %% In OTP/23, the inet_backend option may be used to activate the + %% experimental socket backend for inet/gen_tcp. If present, it must + %% be the first option in the list. + [Backend|prepare_socket_opts(SocketOpts, Logger)]; +prepare_socket_opts(SocketOpts0, Logger) -> SocketOpts1 = ranch:set_option_default(SocketOpts0, backlog, 1024), SocketOpts2 = ranch:set_option_default(SocketOpts1, nodelay, true), SocketOpts3 = ranch:set_option_default(SocketOpts2, send_timeout, 30000), SocketOpts4 = ranch:set_option_default(SocketOpts3, send_timeout_close, true), - %% We set the port to 0 because it is given in the Opts directly. - %% The port in the options takes precedence over the one in the - %% first argument. - gen_tcp:listen(0, ranch:filter_options(SocketOpts4, disallowed_listen_options(), - [binary, {active, false}, {packet, raw}, {reuseaddr, true}], Logger)). + ranch:filter_options(SocketOpts4, disallowed_listen_options(), + [binary, {active, false}, {packet, raw}, {reuseaddr, true}], Logger). %% 'binary' and 'list' are disallowed but they are handled %% specifically as they do not have 2-tuple equivalents. diff --git a/test/acceptor_SUITE.erl b/test/acceptor_SUITE.erl index 1c2cabd..49625c6 100644 --- a/test/acceptor_SUITE.erl +++ b/test/acceptor_SUITE.erl @@ -21,13 +21,14 @@ %% @todo Remove when specs in ssl are updated to accept local addresses. -dialyzer({nowarn_function, do_ssl_local_echo/0}). +-import(ct_helper, [config/2]). -import(ct_helper, [doc/1]). -import(ct_helper, [name/0]). %% ct. all() -> - [{group, tcp}, {group, ssl}, {group, misc}, {group, supervisor}]. + [{group, tcp}, {group, tcp_socket}, {group, ssl}, {group, misc}, {group, supervisor}]. groups() -> [{tcp, [ @@ -51,6 +52,30 @@ groups() -> tcp_many_listen_sockets_no_reuseport, tcp_error_eaddrinuse, tcp_error_eacces + ]}, {tcp_socket, [ + tcp_active_echo, + tcp_active_n_echo, + tcp_echo, + tcp_graceful, + tcp_inherit_options, + tcp_max_connections, + tcp_max_connections_and_beyond, + tcp_max_connections_infinity, + tcp_remove_connections, + tcp_remove_connections_acceptor_wakeup, + tcp_set_max_connections, + tcp_set_max_connections_clean, + tcp_getopts_capability, + tcp_getstat_capability, + tcp_upgrade, + %% @TODO: Enable when https://github.com/erlang/otp/issues/5122 + %% is in an official release, probably 24.1. + % tcp_10_acceptors_10_listen_sockets, + % tcp_many_listen_sockets_no_reuseport, + tcp_error_eaddrinuse + %% @TODO: Not working in OTP/24.0 but fixed in current master. + %% Enable when fixed in an official release, probably 24.1. + % tcp_error_eacces ]}, {ssl, [ ssl_accept_error, ssl_active_echo, @@ -102,6 +127,37 @@ groups() -> supervisor_unexpected_message ]}]. +init_per_group(tcp_socket, Config) -> + %% The socket backend for inet/gen_tcp was introduced as an experimental + %% feature in OTP/23.0, and bugs https://bugs.erlang.org/browse/ERL-1284, + %% 1287 and 1293 were solved in OTP/23.1. socket:use_registry/1 first + %% appears in this release. + %% Due to https://bugs.erlang.org/browse/ERL-1401, the socket backend + %% is not working on Windows. + case + os:type() =/= {win32, nt} andalso + code:ensure_loaded(socket) =:= {module, socket} andalso + erlang:function_exported(socket, use_registry, 1) + of + true -> + [{socket_opts, [{inet_backend, socket}]}|Config]; + false -> + {skip, "No socket backend support"} + end; +init_per_group(_, Config) -> + case + code:ensure_loaded(socket) =:= {module, socket} andalso + erlang:function_exported(socket, use_registry, 1) + of + true -> + [{socket_opts, [{inet_backend, inet}]}|Config]; + false -> + [{socket_opts, []}|Config] + end. + +end_per_group(_, _) -> + ok. + %% misc. misc_bad_transport(_) -> @@ -961,53 +1017,62 @@ do_ssl_unsupported_tlsv13_options() -> %% tcp. -tcp_10_acceptors_10_listen_sockets(_) -> +tcp_10_acceptors_10_listen_sockets(Config) -> case do_os_supports_reuseport() of true -> - ok = do_tcp_10_acceptors_10_listen_sockets(); + ok = do_tcp_10_acceptors_10_listen_sockets(Config); false -> {skip, "No SO_REUSEPORT support."} end. -do_tcp_10_acceptors_10_listen_sockets() -> +do_tcp_10_acceptors_10_listen_sockets(Config) -> doc("Ensure that we can use 10 listen sockets across 10 acceptors with TCP."), Name = name(), - {ok, ListenerSupPid} = ranch:start_listener(Name, + SockOpts = config(socket_opts, Config), + Self = self(), + Tag = make_ref(), + {ok, _} = ranch:start_listener(Name, ranch_tcp, #{ num_acceptors => 10, num_listen_sockets => 10, - socket_opts => [{raw, 1, 15, <<1:32/native>>}]}, + socket_opts => SockOpts ++ [{raw, 1, 15, <<1:32/native>>}], + post_listen_callback => fun (LSocket) -> Self ! {Tag, LSocket}, ok end}, echo_protocol, []), - 10 = length(do_get_listener_sockets(ListenerSupPid)), + LSockets = [receive {Tag, LSocket} -> LSocket after 1000 -> error(timeout) end + || _ <- lists:seq(1, 10)], + 10 = length(LSockets), + 10 = length(lists:usort(LSockets)), ok = ranch:stop_listener(Name), {'EXIT', _} = begin catch ranch:get_port(Name) end, ok. -tcp_many_listen_sockets_no_reuseport(_) -> +tcp_many_listen_sockets_no_reuseport(Config) -> case do_os_supports_reuseport() of true -> - ok = do_tcp_many_listen_sockets_no_reuseport(); + ok = do_tcp_many_listen_sockets_no_reuseport(Config); false -> {skip, "No SO_REUSEPORT support."} end. -do_tcp_many_listen_sockets_no_reuseport() -> +do_tcp_many_listen_sockets_no_reuseport(Config) -> doc("Confirm that ranch:start_listener/5 fails when SO_REUSEPORT is not available with TCP."), Name = name(), + SockOpts = config(socket_opts, Config), {error, eaddrinuse} = ranch:start_listener(Name, ranch_tcp, #{ num_acceptors => 10, num_listen_sockets => 10, - socket_opts => [{raw, 1, 15, <<0:32/native>>}]}, + socket_opts => SockOpts ++ [{raw, 1, 15, <<0:32/native>>}]}, echo_protocol, []), {'EXIT', _} = begin catch ranch:get_port(Name) end, ok. -tcp_active_echo(_) -> +tcp_active_echo(Config) -> doc("Ensure that active mode works with TCP transport."), Name = name(), + SockOpts = config(socket_opts, Config), {ok, _} = ranch:start_listener(Name, - ranch_tcp, #{}, + ranch_tcp, #{socket_opts => SockOpts}, active_echo_protocol, []), Port = ranch:get_port(Name), {ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]), @@ -1019,11 +1084,12 @@ tcp_active_echo(_) -> {'EXIT', _} = begin catch ranch:get_port(Name) end, ok. -tcp_active_n_echo(_) -> +tcp_active_n_echo(Config) -> doc("Ensure that active N mode works with TCP transport."), Name = name(), + SockOpts = config(socket_opts, Config), {ok, _} = ranch:start_listener(Name, - ranch_tcp, #{}, + ranch_tcp, #{socket_opts => SockOpts}, batch_echo_protocol, [{batch_size, 3}]), Port = ranch:get_port(Name), {ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]), @@ -1040,11 +1106,12 @@ tcp_active_n_echo(_) -> {'EXIT', _} = begin catch ranch:get_port(Name) end, ok. -tcp_echo(_) -> +tcp_echo(Config) -> doc("Ensure that passive mode works with TCP transport."), Name = name(), + SockOpts = config(socket_opts, Config), {ok, _} = ranch:start_listener(Name, - ranch_tcp, #{}, + ranch_tcp, #{socket_opts => SockOpts}, echo_protocol, []), Port = ranch:get_port(Name), {ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]), @@ -1087,11 +1154,12 @@ do_tcp_local_echo() -> file:delete(SockFile) end. -tcp_graceful(_) -> +tcp_graceful(Config) -> doc("Ensure suspending and resuming of listeners does not kill active connections."), Name = name(), + SockOpts = config(socket_opts, Config), {ok, _} = ranch:start_listener(Name, - ranch_tcp, #{}, + ranch_tcp, #{socket_opts => SockOpts}, echo_protocol, []), Port = ranch:get_port(Name), %% Make sure connections with a fresh listener work. @@ -1123,24 +1191,26 @@ tcp_graceful(_) -> {'EXIT', _} = begin catch ranch:get_port(Name) end, ok. -tcp_inherit_options(_) -> +tcp_inherit_options(Config) -> doc("Ensure TCP options are inherited in the protocol."), Name = name(), - Opts = [{nodelay, false}, {send_timeout_close, false}], + Opts0 = config(socket_opts, Config), + Opts1 = [{nodelay, false}, {send_timeout_close, false}], {ok, _} = ranch:start_listener(Name, - ranch_tcp, Opts, - check_tcp_options, [{pid, self()} | Opts]), + ranch_tcp, #{socket_opts => Opts0 ++ Opts1}, + check_tcp_options, [{pid, self()} | Opts1]), Port = ranch:get_port(Name), {ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {active, true}, {packet, raw}]), receive checked -> ok after 1000 -> error(timeout) end, ok = gen_tcp:close(Socket), ok = ranch:stop_listener(Name). -tcp_max_connections(_) -> +tcp_max_connections(Config) -> doc("Ensure the max_connections option actually limits connections."), Name = name(), + SockOpts = config(socket_opts, Config), {ok, _} = ranch:start_listener(Name, - ranch_tcp, #{max_connections => 10, num_acceptors => 1}, + ranch_tcp, #{max_connections => 10, num_acceptors => 1, socket_opts => SockOpts}, notify_and_wait_protocol, #{pid => self()}), Port = ranch:get_port(Name), ok = connect_loop(Port, 11, 150), @@ -1151,11 +1221,12 @@ tcp_max_connections(_) -> ok = terminate_loop(stop, Pids2), ok = ranch:stop_listener(Name). -tcp_max_connections_and_beyond(_) -> +tcp_max_connections_and_beyond(Config) -> doc("Ensure the max_connections option works when connections are removed from the count."), Name = name(), + SockOpts = config(socket_opts, Config), {ok, _} = ranch:start_listener(Name, - ranch_tcp, #{max_connections => 10, num_acceptors => 1}, + ranch_tcp, #{max_connections => 10, num_acceptors => 1, socket_opts => SockOpts}, remove_conn_and_wait_protocol, [{remove, true, 2500}]), Port = ranch:get_port(Name), ok = connect_loop(Port, 10, 0), @@ -1178,11 +1249,12 @@ tcp_max_connections_and_beyond(_) -> {_, 20} = lists:keyfind(workers, 1, Counts2), ok = ranch:stop_listener(Name). -tcp_max_connections_infinity(_) -> +tcp_max_connections_infinity(Config) -> doc("Set the max_connections option from 10 to infinity and back to 10."), Name = name(), + SockOpts = config(socket_opts, Config), {ok, _} = ranch:start_listener(Name, - ranch_tcp, #{max_connections => 10, num_acceptors => 1}, + ranch_tcp, #{max_connections => 10, num_acceptors => 1, socket_opts => SockOpts}, notify_and_wait_protocol, #{pid => self()}), Port = ranch:get_port(Name), ok = connect_loop(Port, 20, 0), @@ -1200,11 +1272,12 @@ tcp_max_connections_infinity(_) -> ok = terminate_loop(stop, Pids1 ++ Pids2), ok = ranch:stop_listener(Name). -tcp_remove_connections(_) -> +tcp_remove_connections(Config) -> doc("Ensure that removed connections are only removed once."), Name = name(), + SockOpts = config(socket_opts, Config), {ok, _} = ranch:start_listener(Name, - ranch_tcp, #{}, + ranch_tcp, #{socket_opts => SockOpts}, remove_conn_and_wait_protocol, [{remove, true, 0}]), Port = ranch:get_port(Name), ok = connect_loop(Port, 10, 0), @@ -1212,11 +1285,12 @@ tcp_remove_connections(_) -> 0 = ranch_server:count_connections(Name), ok = ranch:stop_listener(Name). -tcp_remove_connections_acceptor_wakeup(_) -> +tcp_remove_connections_acceptor_wakeup(Config) -> doc("Ensure that removed connections wake up acceptors."), Name = name(), + SockOpts = config(socket_opts, Config), {ok, _} = ranch:start_listener(Name, - ranch_tcp, #{max_connections => 1, num_acceptors => 1}, + ranch_tcp, #{max_connections => 1, num_acceptors => 1, socket_opts => SockOpts}, remove_conn_and_wait_protocol, [{remove, true, infinity}]), Port = ranch:get_port(Name), ConnectOptions = [binary, {active, false}], @@ -1230,11 +1304,12 @@ tcp_remove_connections_acceptor_wakeup(_) -> ok = gen_tcp:send(Socket2, <<"bye">>), ok = ranch:stop_listener(Name). -tcp_set_max_connections(_) -> +tcp_set_max_connections(Config) -> doc("Ensure that changing the max_connections option to a larger value allows for more connections."), Name = name(), + SockOpts = config(socket_opts, Config), {ok, _} = ranch:start_listener(Name, - ranch_tcp, #{max_connections => 10, num_acceptors => 1}, + ranch_tcp, #{max_connections => 10, num_acceptors => 1, socket_opts => SockOpts}, notify_and_wait_protocol, #{pid => self()}), Port = ranch:get_port(Name), ok = connect_loop(Port, 20, 0), @@ -1247,11 +1322,12 @@ tcp_set_max_connections(_) -> ok = terminate_loop(stop, Pids1 ++ Pids2), ok = ranch:stop_listener(Name). -tcp_set_max_connections_clean(_) -> +tcp_set_max_connections_clean(Config) -> doc("Ensure that setting max_connections does not crash any process."), Name = name(), + SockOpts = config(socket_opts, Config), {ok, ListSupPid} = ranch:start_listener(Name, - ranch_tcp, #{max_connections => 4}, + ranch_tcp, #{max_connections => 4, socket_opts => SockOpts}, notify_and_wait_protocol, #{pid => self()}), Children = supervisor:which_children(ListSupPid), {_, AccSupPid, _, _} = lists:keyfind(ranch_acceptors_sup, 1, Children), @@ -1273,11 +1349,12 @@ tcp_set_max_connections_clean(_) -> ok = clean_traces(), ok = ranch:stop_listener(Name). -tcp_getopts_capability(_) -> +tcp_getopts_capability(Config) -> doc("Ensure getopts/2 capability."), Name=name(), + SockOpts = config(socket_opts, Config), {ok, _}=ranch:start_listener(Name, - ranch_tcp, #{}, + ranch_tcp, #{socket_opts => SockOpts}, transport_capabilities_protocol, []), Port=ranch:get_port(Name), {ok, Socket}=gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]), @@ -1288,11 +1365,12 @@ tcp_getopts_capability(_) -> {'EXIT', _}=begin catch ranch:get_port(Name) end, ok. -tcp_getstat_capability(_) -> +tcp_getstat_capability(Config) -> doc("Ensure getstat/1,2 capability."), Name=name(), + SockOpts = config(socket_opts, Config), {ok, _}=ranch:start_listener(Name, - ranch_tcp, #{}, + ranch_tcp, #{socket_opts => SockOpts}, transport_capabilities_protocol, []), Port=ranch:get_port(Name), {ok, Socket}=gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]), @@ -1305,11 +1383,12 @@ tcp_getstat_capability(_) -> {'EXIT', _}=begin catch ranch:get_port(Name) end, ok. -tcp_upgrade(_) -> +tcp_upgrade(Config) -> doc("Ensure that protocol options can be updated."), Name = name(), + SockOpts = config(socket_opts, Config), {ok, _} = ranch:start_listener(Name, - ranch_tcp, #{}, + ranch_tcp, #{socket_opts => SockOpts}, notify_and_wait_protocol, #{pid => self()}), Port = ranch:get_port(Name), ok = connect_loop(Port, 1, 0), @@ -1320,11 +1399,12 @@ tcp_upgrade(_) -> ok = terminate_loop(stop, Pids1 ++ Pids2), ok = ranch:stop_listener(Name). -tcp_error_eaddrinuse(_) -> +tcp_error_eaddrinuse(Config) -> doc("Ensure that failure due to an eaddrinuse returns a compact readable error."), Name = name(), + SockOpts = config(socket_opts, Config), {ok, _} = ranch:start_listener(Name, - ranch_tcp, #{}, + ranch_tcp, #{socket_opts => SockOpts}, active_echo_protocol, []), Port = ranch:get_port(Name), {error, eaddrinuse} = ranch:start_listener({Name, fails}, @@ -1335,7 +1415,7 @@ tcp_error_eaddrinuse(_) -> {'EXIT', _} = begin catch ranch:get_port(Name) end, ok. -tcp_error_eacces(_) -> +tcp_error_eacces(Config) -> case os:type() of {win32, nt} -> {skip, "No privileged ports."}; @@ -1344,8 +1424,9 @@ tcp_error_eacces(_) -> _ -> doc("Ensure that failure due to an eacces returns a compact readable error."), Name = name(), + SockOpts = config(socket_opts, Config), {error, eacces} = ranch:start_listener(Name, - ranch_tcp, [{port, 283}], + ranch_tcp, #{socket_opts => SockOpts ++ [{port, 283}]}, active_echo_protocol, []), ok end. diff --git a/test/ranch_ct_hook.erl b/test/ranch_ct_hook.erl index a8b6885..d051c37 100644 --- a/test/ranch_ct_hook.erl +++ b/test/ranch_ct_hook.erl @@ -32,6 +32,8 @@ init(_, _) -> ok; {{win32, nt}, {ok, "9."++_}} -> application:set_env(ssl, internal_active_n, 1); + {{win32, nt}, {ok, "10."++_}} -> + application:set_env(ssl, internal_active_n, 1); _ -> ok end, diff --git a/test/sendfile_SUITE.erl b/test/sendfile_SUITE.erl index ca3242c..c787542 100644 --- a/test/sendfile_SUITE.erl +++ b/test/sendfile_SUITE.erl @@ -21,7 +21,7 @@ -import(ct_helper, [doc/1]). all() -> - [{group, tcp}, {group, ssl}]. + [{group, tcp}, {group, tcp_socket}, {group, ssl}]. suite() -> [{timetrap, {seconds, 60}}]. @@ -38,7 +38,11 @@ groups() -> rawfile_range_medium, rawfile_range_small ], - [{tcp, [parallel], Tests}, {ssl, [parallel], Tests ++ [ssl_chunk_size]}]. + [ + {tcp, [parallel], Tests}, + {tcp_socket, [parallel], Tests}, + {ssl, [parallel], Tests ++ [ssl_chunk_size]} + ]. init_per_suite(Config) -> Filename = filename:join(config(priv_dir, Config), "sendfile"), @@ -55,7 +59,24 @@ init_per_group(ssl, Config) -> SslOpts = ct_helper:get_certs_from_ets(), [{transport, ranch_ssl}, {transport_opts, SslOpts} | Config]; init_per_group(tcp, Config) -> - [{transport, ranch_tcp}, {transport_opts, []} | Config]. + [{transport, ranch_tcp}, {transport_opts, []} | Config]; +init_per_group(tcp_socket, Config) -> + %% The socket backend for inet/gen_tcp was introduced as an experimental + %% feature in OTP/23.0, and bugs https://bugs.erlang.org/browse/ERL-1284, + %% 1287 and 1293 were solved in OTP/23.1. socket:use_registry/1 first + %% appears in this release. + %% Due to https://bugs.erlang.org/browse/ERL-1401, the socket backend + %% is not working on Windows. + case + os:type() =/= {win32, nt} andalso + code:ensure_loaded(socket) =:= {module, socket} andalso + erlang:function_exported(socket, use_registry, 1) + of + true -> + [{transport, ranch_tcp}, {transport_opts, [{inet_backend, socket}]} | Config]; + false -> + {skip, "No socket backend support"} + end. end_per_group(_, _) -> ok. diff --git a/test/stampede_SUITE.erl b/test/stampede_SUITE.erl index cdda863..45d29de 100644 --- a/test/stampede_SUITE.erl +++ b/test/stampede_SUITE.erl @@ -22,7 +22,57 @@ %% ct. all() -> - ct_helper:all(?MODULE). + [{group, tcp}, {group, tcp_socket}, {group, ssl}]. + +groups() -> + [ + { + tcp, + [], + [ + stampede_tcp, + stampede_embedded + ] + }, + { + tcp_socket, + [], + [ + stampede_tcp, + stampede_embedded + ] + }, + { + ssl, + [], + [ + stampede_ssl + ] + } + ]. + +init_per_group(tcp_socket, Config) -> + %% The socket backend for inet/gen_tcp was introduced as an experimental + %% feature in OTP/23.0, and bugs https://bugs.erlang.org/browse/ERL-1284, + %% 1287 and 1293 were solved in OTP/23.1. socket:use_registry/1 first + %% appears in this release. + %% Due to https://bugs.erlang.org/browse/ERL-1401, the socket backend + %% is not working on Windows. + case + os:type() =/= {win32, nt} andalso + code:ensure_loaded(socket) =:= {module, socket} andalso + erlang:function_exported(socket, use_registry, 1) + of + true -> + [{socket_opts, [{inet_backend, socket}]}|Config]; + false -> + {skip, "No socket backend support"} + end; +init_per_group(_, Config) -> + Config. + +end_per_group(_, _) -> + ok. init_per_suite(Config) -> {ok, _} = application:ensure_all_started(ranch), @@ -40,21 +90,22 @@ end_per_suite(_) -> %% Tests. -stampede_tcp(_) -> +stampede_tcp(Config) -> doc("Start a TCP listener, establish a hundred connections, " "run stampede, confirm we can still connect."), %% Start a TCP listener. Name = name(), + SockOpts = do_get_sockopts(Config), {ok, _} = ranch:start_listener(Name, - ranch_tcp, #{}, + ranch_tcp, #{socket_opts => SockOpts}, echo_protocol, []), - %% Establish a hundred connections. - ok = do_connect(100, ranch_tcp, ranch:get_port(Name), 1000), %% Set restart frequency of ranch_sup. do_set_sup_frequencies([ranch_sup], 999999, 1), %% Run stampede. {ok, _} = stampede:start_herd(ranch_stampede, {application, ranch}, #{interval => {100, 100}, before_kill => fun do_log/1}), + %% Establish a hundred connections. + ok = do_connect(100, ranch_tcp, ranch:get_port(Name), 1000), ok = stampede:activate(ranch_stampede), timer:sleep(10000), ok = stampede:stop_herd(ranch_stampede), @@ -71,8 +122,6 @@ stampede_ssl(_) -> {ok, _} = ranch:start_listener(Name, ranch_ssl, ct_helper:get_certs_from_ets(), echo_protocol, []), - %% Establish a hundred connections. - ok = do_connect(100, ranch_ssl, ranch:get_port(Name), 1000), %% Set restart frequencies of ranch_sup and ssl_sup. do_set_sup_frequencies([ranch_sup, ssl_sup], 999999, 1), %% Run stampede. @@ -80,6 +129,8 @@ stampede_ssl(_) -> #{interval => {100, 100}, before_kill => fun do_log/1}), {ok, _} = stampede:start_herd(ssl_stampede, {application, ssl}, #{interval => {100, 100}, before_kill => fun do_log/1}), + %% Establish a hundred connections. + ok = do_connect(100, ranch_ssl, ranch:get_port(Name), 1000), ok = stampede:activate(ranch_stampede), ok = stampede:activate(ssl_stampede), timer:sleep(10000), @@ -90,16 +141,15 @@ stampede_ssl(_) -> ok = do_connect(1, ranch_ssl, ranch:get_port(Name), 1000), ok = ranch:stop_listener(Name). -stampede_embedded(_) -> +stampede_embedded(Config) -> doc("Start an embedded TCP listener, establish a hundred connections, " "run stampede, confirm we can still connect."), %% Start embedded listener. Name = name(), + SockOpts = do_get_sockopts(Config), {ok, SupPid} = embedded_sup:start_link(), {ok, _} = embedded_sup:start_listener(SupPid, Name, - ranch_tcp, #{}, echo_protocol, []), - %% Establish a hundred connections. - ok = do_connect(100, ranch_tcp, ranch:get_port(Name), 1000), + ranch_tcp, #{socket_opts => SockOpts}, echo_protocol, []), %% Set restart frequency of ranch_sup and embedded_sup. do_set_sup_frequencies([ranch_sup, SupPid], 999999, 1), %% Run stampede. @@ -107,6 +157,8 @@ stampede_embedded(_) -> #{interval => {100, 100}, before_kill => fun do_log/1}), {ok, _} = stampede:start_herd(embedded_stampede, {supervisor, SupPid}, #{interval => {100, 100}, before_kill => fun do_log/1}), + %% Establish a hundred connections. + ok = do_connect(100, ranch_tcp, ranch:get_port(Name), 1000), ok = stampede:activate(ranch_stampede), ok = stampede:activate(embedded_stampede), timer:sleep(10000), @@ -136,3 +188,6 @@ do_log(Pid) when is_pid(Pid) -> do_log(Port) when is_port(Port) -> ct:log(info, "~p: ~p~n", [Port, erlang:port_info(Port)]), true. + +do_get_sockopts(Config) -> + proplists:get_value(socket_opts, Config, []). -- cgit v1.2.3