From 30604262b5934b38e9f55f0d3ef44f8aed5310de Mon Sep 17 00:00:00 2001 From: juhlig Date: Mon, 27 May 2019 15:20:37 +0200 Subject: Use transport options in ranch_transport:listen/1 callbacks The callback `ranch_transport:listen/1` has changed to accept a map of transport options instead of socket options. --- doc/src/guide/upcoming_2.0_changes.asciidoc | 5 +++ doc/src/manual/ranch.asciidoc | 43 ++++++++++++++-------- doc/src/manual/ranch_transport.asciidoc | 7 +++- src/ranch.erl | 57 ++++++++++++++--------------- src/ranch_acceptors_sup.erl | 37 ++++++++++--------- src/ranch_ssl.erl | 30 ++++++++------- src/ranch_tcp.erl | 18 +++++---- src/ranch_transport.erl | 2 +- test/sendfile_SUITE.erl | 2 +- 9 files changed, 113 insertions(+), 88 deletions(-) diff --git a/doc/src/guide/upcoming_2.0_changes.asciidoc b/doc/src/guide/upcoming_2.0_changes.asciidoc index d743090..378d0a6 100644 --- a/doc/src/guide/upcoming_2.0_changes.asciidoc +++ b/doc/src/guide/upcoming_2.0_changes.asciidoc @@ -5,6 +5,11 @@ The following changes will be done in Ranch 2.0. In most cases an alternative is already available in the most recent Ranch version. +* The callback function `ranch_transport:listen/1` and its + implementations in `ranch_tcp` and `ranch_ssl` have changed + to accept a map of transport options instead of socket + options. + * The function `ranch:start_listener/6` has been deprecated in favor of `ranch:start_listener/5`. The number of acceptors was removed and will be taken from the transport options. diff --git a/doc/src/manual/ranch.asciidoc b/doc/src/manual/ranch.asciidoc index 549bb78..065ded3 100644 --- a/doc/src/manual/ranch.asciidoc +++ b/doc/src/manual/ranch.asciidoc @@ -67,7 +67,30 @@ code. [source,erlang] ---- -opts() = any() | #{ +opts() = any() | transport_opts(any()) +---- + +Transport or socket options. + +It is possible to give the full transport options in a map +(see `transport_opts(SocketOpts)`), or only the socket options +(assuming they are not a map and no Ranch-specific option +needs to be given). + +=== ref() + +[source,erlang] +---- +ref() = any() +---- + +Unique name used to refer to a listener. + +=== transport_opts(SocketOpts) + +[source,erlang] +---- +transport_opts(SocketOpts) = #{ connection_type => worker | supervisor, handshake_timeout => timeout(), max_connections => max_conns(), @@ -75,16 +98,14 @@ opts() = any() | #{ num_acceptors => pos_integer(), num_conns_sups => pos_integer(), shutdown => timeout() | brutal_kill, - socket_opts => any() + socket_opts => SocketOpts } ---- Transport options. The transport options are a combination of Ranch-specific -options and socket options. Socket options may be given -directly (assuming they are not a map and no Ranch-specific -option needs to be given) or as part of `socket_opts`. +options and transport-specific socket options. None of the options are required. @@ -120,20 +141,12 @@ Maximum allowed time for children to stop on listener shutdown. socket_opts:: -Socket options given to `Transport:listen/1`. Please refer to the +Socket options to be used by `Transport:listen/1`. Please refer to the documentation of the transport module you are using for more details. -=== ref() - -[source,erlang] ----- -ref() = any() ----- - -Unique name used to refer to a listener. - == Changelog +* *2.0*: The type `transport_opts(SocketOpts)` was added. * *2.0*: The function `ranch:accept_ack/1` was removed in favor of link:man:ranch:handshake(3)[ranch:handshake(3)]. * *2.0*: The option `max_connections` is now per connection supervisor. diff --git a/doc/src/manual/ranch_transport.asciidoc b/doc/src/manual/ranch_transport.asciidoc index 2d59e82..78468b3 100644 --- a/doc/src/manual/ranch_transport.asciidoc +++ b/doc/src/manual/ranch_transport.asciidoc @@ -101,11 +101,12 @@ be upgraded to a `ranch_ssl` one using this function. [source,erlang] ---- -listen(SockOpts :: any()) +listen(TransportOpts :: ranch:transport_opts(any())) -> {ok, LSocket :: socket()} | {error, atom()} ---- -Create a socket that listens on the given port. +Create a socket that listens on the port given in the +socket options. The port may not be specified or may be set to 0, which means a random available port number will be chosen. @@ -292,6 +293,8 @@ The exact type will vary depending on the transport module. == Changelog +* *2.0*: The callback `listen/1` has changed to accept a map of + transport options instead of socket options. * *2.0*: The callback `messages/0` return value was updated to include the passive message for `{active, N}`. * *1.6*: The `socket()` type was added for documentation purposes. diff --git a/src/ranch.erl b/src/ranch.erl index 86a1c30..0580d5b 100644 --- a/src/ranch.erl +++ b/src/ranch.erl @@ -38,7 +38,7 @@ -export([procs/2]). -export([wait_for_connections/3]). -export([wait_for_connections/4]). --export([filter_options/3]). +-export([filter_options/4]). -export([set_option_default/3]). -export([require/1]). -export([log/4]). @@ -46,7 +46,10 @@ -type max_conns() :: non_neg_integer() | infinity. -export_type([max_conns/0]). --type opts() :: any() | #{ +-type opts() :: any() | transport_opts(any()). +-export_type([opts/0]). + +-type transport_opts(SocketOpts) :: #{ connection_type => worker | supervisor, handshake_timeout => timeout(), max_connections => max_conns(), @@ -55,9 +58,9 @@ num_conns_sups => pos_integer(), num_listen_sockets => pos_integer(), shutdown => timeout() | brutal_kill, - socket_opts => any() + socket_opts => SocketOpts }. --export_type([opts/0]). +-export_type([transport_opts/1]). -type ref() :: any(). -export_type([ref/0]). @@ -76,7 +79,7 @@ start_listener(Ref, Transport, TransOpts0, Protocol, ProtoOpts) Transport, TransOpts, Protocol, ProtoOpts))) end. --spec normalize_opts(opts()) -> opts(). +-spec normalize_opts(opts()) -> transport_opts(any()). normalize_opts(Map) when is_map(Map) -> Map; normalize_opts(Any) -> @@ -233,7 +236,7 @@ get_max_connections(Ref) -> set_max_connections(Ref, MaxConnections) -> ranch_server:set_max_connections(Ref, MaxConnections). --spec get_transport_options(ref()) -> any(). +-spec get_transport_options(ref()) -> transport_opts(any()). get_transport_options(Ref) -> ranch_server:get_transport_options(Ref). @@ -247,7 +250,7 @@ set_transport_options(Ref, TransOpts0) -> {error, running} end. --spec get_protocol_options(ref()) -> opts(). +-spec get_protocol_options(ref()) -> any(). get_protocol_options(Ref) -> ranch_server:get_protocol_options(Ref). @@ -360,38 +363,34 @@ wait_for_connections_loop(Ref, Op, NumConns, Interval) -> end. -spec filter_options([inet | inet6 | {atom(), any()} | {raw, any(), any(), any()}], - [atom()], Acc) -> Acc when Acc :: [any()]. -filter_options(UserOptions, DisallowedKeys, DefaultOptions) -> - AllowedOptions = filter_user_options(UserOptions, DisallowedKeys), + [atom()], Acc, module()) -> Acc when Acc :: [any()]. +filter_options(UserOptions, DisallowedKeys, DefaultOptions, Logger) -> + AllowedOptions = filter_user_options(UserOptions, DisallowedKeys, Logger), lists:foldl(fun merge_options/2, DefaultOptions, AllowedOptions). %% 2-tuple options. -filter_user_options([Opt = {Key, _}|Tail], DisallowedKeys) -> +filter_user_options([Opt = {Key, _}|Tail], DisallowedKeys, Logger) -> case lists:member(Key, DisallowedKeys) of false -> - [Opt|filter_user_options(Tail, DisallowedKeys)]; + [Opt|filter_user_options(Tail, DisallowedKeys, Logger)]; true -> - filter_options_warning(Opt), - filter_user_options(Tail, DisallowedKeys) + filter_options_warning(Opt, Logger), + filter_user_options(Tail, DisallowedKeys, Logger) end; %% Special option forms. -filter_user_options([inet|Tail], DisallowedKeys) -> - [inet|filter_user_options(Tail, DisallowedKeys)]; -filter_user_options([inet6|Tail], DisallowedKeys) -> - [inet6|filter_user_options(Tail, DisallowedKeys)]; -filter_user_options([Opt = {raw, _, _, _}|Tail], DisallowedKeys) -> - [Opt|filter_user_options(Tail, DisallowedKeys)]; -filter_user_options([Opt|Tail], DisallowedKeys) -> - filter_options_warning(Opt), - filter_user_options(Tail, DisallowedKeys); -filter_user_options([], _) -> +filter_user_options([inet|Tail], DisallowedKeys, Logger) -> + [inet|filter_user_options(Tail, DisallowedKeys, Logger)]; +filter_user_options([inet6|Tail], DisallowedKeys, Logger) -> + [inet6|filter_user_options(Tail, DisallowedKeys, Logger)]; +filter_user_options([Opt = {raw, _, _, _}|Tail], DisallowedKeys, Logger) -> + [Opt|filter_user_options(Tail, DisallowedKeys, Logger)]; +filter_user_options([Opt|Tail], DisallowedKeys, Logger) -> + filter_options_warning(Opt, Logger), + filter_user_options(Tail, DisallowedKeys, Logger); +filter_user_options([], _, _) -> []. -filter_options_warning(Opt) -> - Logger = case get(logger) of - undefined -> logger; - Logger0 -> Logger0 - end, +filter_options_warning(Opt, Logger) -> log(warning, "Transport option ~p unknown or invalid.~n", [Opt], Logger). diff --git a/src/ranch_acceptors_sup.erl b/src/ranch_acceptors_sup.erl index c062645..a1bb8c2 100644 --- a/src/ranch_acceptors_sup.erl +++ b/src/ranch_acceptors_sup.erl @@ -27,14 +27,11 @@ init([Ref, NumAcceptors, Transport]) -> TransOpts = ranch_server:get_transport_options(Ref), Logger = maps:get(logger, TransOpts, logger), NumListenSockets = maps:get(num_listen_sockets, TransOpts, 1), - SocketOpts = maps:get(socket_opts, TransOpts, []), %% We temporarily put the logger in the process dictionary %% so that it can be used from ranch:filter_options. The %% interface as it currently is does not allow passing it %% down otherwise. - put(logger, Logger), - LSockets = start_listen_sockets(Ref, NumListenSockets, Transport, SocketOpts, Logger), - erase(logger), + LSockets = start_listen_sockets(Ref, NumListenSockets, Transport, TransOpts, Logger), Procs = [begin LSocketId = (AcceptorId rem NumListenSockets) + 1, {_, LSocket} = lists:keyfind(LSocketId, 1, LSockets), @@ -46,46 +43,50 @@ init([Ref, NumAcceptors, Transport]) -> end || AcceptorId <- lists:seq(1, NumAcceptors)], {ok, {#{}, Procs}}. --spec start_listen_sockets(any(), pos_integer(), module(), list(), module()) +-spec start_listen_sockets(any(), pos_integer(), module(), map(), module()) -> [{pos_integer(), inet:socket()}]. -start_listen_sockets(Ref, NumListenSockets, Transport, SocketOpts0, Logger) when NumListenSockets > 0 -> - BaseSocket = start_listen_socket(Ref, Transport, SocketOpts0, Logger), +start_listen_sockets(Ref, NumListenSockets, Transport, TransOpts0, Logger) when NumListenSockets > 0 -> + BaseSocket = start_listen_socket(Ref, Transport, TransOpts0, Logger), {ok, Addr} = Transport:sockname(BaseSocket), ExtraSockets = case Addr of {local, _} when NumListenSockets > 1 -> - listen_error(Ref, Transport, SocketOpts0, reuseport_local, Logger); + listen_error(Ref, Transport, TransOpts0, reuseport_local, Logger); {local, _} -> []; {_, Port} -> - SocketOpts = case lists:keyfind(port, 1, SocketOpts0) of + SocketOpts = maps:get(socket_opts, TransOpts0, []), + SocketOpts1 = case lists:keyfind(port, 1, SocketOpts) of {port, Port} -> - SocketOpts0; + SocketOpts; _ -> - [{port, Port}|lists:keydelete(port, 1, SocketOpts0)] + [{port, Port}|lists:keydelete(port, 1, SocketOpts)] end, - [{N, start_listen_socket(Ref, Transport, SocketOpts, Logger)} + TransOpts1 = TransOpts0#{socket_opts => SocketOpts1}, + [{N, start_listen_socket(Ref, Transport, TransOpts1, Logger)} || N <- lists:seq(2, NumListenSockets)] end, ranch_server:set_addr(Ref, Addr), [{1, BaseSocket}|ExtraSockets]. --spec start_listen_socket(any(), module(), list(), module()) -> inet:socket(). -start_listen_socket(Ref, Transport, SocketOpts, Logger) -> - case Transport:listen(SocketOpts) of +-spec start_listen_socket(any(), module(), map(), module()) -> inet:socket(). +start_listen_socket(Ref, Transport, TransOpts, Logger) -> + case Transport:listen(TransOpts) of {ok, Socket} -> Socket; {error, Reason} -> - listen_error(Ref, Transport, SocketOpts, Reason, Logger) + listen_error(Ref, Transport, TransOpts, Reason, Logger) end. -spec listen_error(any(), module(), any(), atom(), module()) -> no_return(). -listen_error(Ref, Transport, SocketOpts0, Reason, Logger) -> +listen_error(Ref, Transport, TransOpts0, Reason, Logger) -> + SocketOpts0 = maps:get(socket_opts, TransOpts0, []), SocketOpts1 = [{cert, '...'}|proplists:delete(cert, SocketOpts0)], SocketOpts2 = [{key, '...'}|proplists:delete(key, SocketOpts1)], SocketOpts = [{cacerts, '...'}|proplists:delete(cacerts, SocketOpts2)], + TransOpts = TransOpts0#{socket_opts => SocketOpts}, ranch:log(error, "Failed to start Ranch listener ~p in ~p:listen(~999999p) for reason ~p (~s)~n", - [Ref, Transport, SocketOpts, Reason, format_error(Reason)], Logger), + [Ref, Transport, TransOpts, Reason, format_error(Reason)], Logger), exit({listen_error, Ref, Reason}). format_error(no_cert) -> diff --git a/src/ranch_ssl.erl b/src/ranch_ssl.erl index bcbe909..cf876fe 100644 --- a/src/ranch_ssl.erl +++ b/src/ranch_ssl.erl @@ -98,28 +98,30 @@ secure() -> messages() -> {ssl, ssl_closed, ssl_error, ssl_passive}. --spec listen(opts()) -> {ok, ssl:sslsocket()} | {error, atom()}. -listen(Opts) -> - case lists:keymember(cert, 1, Opts) - orelse lists:keymember(certfile, 1, Opts) - orelse lists:keymember(sni_fun, 1, Opts) - orelse lists:keymember(sni_hosts, 1, Opts) of +-spec listen(ranch:transport_opts(opts())) -> {ok, ssl:sslsocket()} | {error, atom()}. +listen(TransOpts) -> + SocketOpts = maps:get(socket_opts, TransOpts, []), + case lists:keymember(cert, 1, SocketOpts) + orelse lists:keymember(certfile, 1, SocketOpts) + orelse lists:keymember(sni_fun, 1, SocketOpts) + orelse lists:keymember(sni_hosts, 1, SocketOpts) of true -> - do_listen(Opts); + Logger = maps:get(logger, TransOpts, logger), + do_listen(SocketOpts, Logger); false -> {error, no_cert} end. -do_listen(Opts0) -> - Opts1 = ranch:set_option_default(Opts0, backlog, 1024), - Opts2 = ranch:set_option_default(Opts1, nodelay, true), - Opts3 = ranch:set_option_default(Opts2, send_timeout, 30000), - Opts = ranch:set_option_default(Opts3, send_timeout_close, true), +do_listen(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), + SocketOpts = 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. - ssl:listen(0, ranch:filter_options(Opts, disallowed_listen_options(), - [binary, {active, false}, {packet, raw}, {reuseaddr, true}])). + ssl:listen(0, ranch:filter_options(SocketOpts, 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/src/ranch_tcp.erl b/src/ranch_tcp.erl index 764c4b8..3a6e4d9 100644 --- a/src/ranch_tcp.erl +++ b/src/ranch_tcp.erl @@ -78,17 +78,19 @@ secure() -> messages() -> {tcp, tcp_closed, tcp_error, tcp_passive}. --spec listen(opts()) -> {ok, inet:socket()} | {error, atom()}. -listen(Opts) -> - Opts2 = ranch:set_option_default(Opts, backlog, 1024), - Opts3 = ranch:set_option_default(Opts2, nodelay, true), - Opts4 = ranch:set_option_default(Opts3, send_timeout, 30000), - Opts5 = ranch:set_option_default(Opts4, send_timeout_close, true), +-spec listen(ranch:transport_opts(opts())) -> {ok, inet:socket()} | {error, atom()}. +listen(TransOpts) -> + Logger = maps:get(logger, TransOpts, logger), + SocketOpts0 = maps:get(socket_opts, TransOpts, []), + 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(Opts5, disallowed_listen_options(), - [binary, {active, false}, {packet, raw}, {reuseaddr, true}])). + gen_tcp:listen(0, 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/src/ranch_transport.erl b/src/ranch_transport.erl index 2344733..6790977 100644 --- a/src/ranch_transport.erl +++ b/src/ranch_transport.erl @@ -27,7 +27,7 @@ -callback name() -> atom(). -callback secure() -> boolean(). -callback messages() -> {OK::atom(), Closed::atom(), Error::atom(), Passive::atom()}. --callback listen(opts()) -> {ok, socket()} | {error, atom()}. +-callback listen(ranch:transport_opts(any())) -> {ok, socket()} | {error, atom()}. -callback accept(socket(), timeout()) -> {ok, socket()} | {error, closed | timeout | atom()}. -callback handshake(socket(), opts(), timeout()) -> {ok, socket()} | {error, any()}. diff --git a/test/sendfile_SUITE.erl b/test/sendfile_SUITE.erl index 5a6d2cb..97716f7 100644 --- a/test/sendfile_SUITE.erl +++ b/test/sendfile_SUITE.erl @@ -270,7 +270,7 @@ do_ssl_chunk_size(Config) -> sockets(Config) -> Transport = config(transport, Config), TransportOpts = config(transport_opts, Config), - {ok, LSocket} = Transport:listen(TransportOpts), + {ok, LSocket} = Transport:listen(#{socket_opts => TransportOpts}), {ok, {_, Port}} = Transport:sockname(LSocket), Self = self(), Fun = fun() -> -- cgit v1.2.3