From bac2faea84357980ce20d5ebcaa34f0550e0447a Mon Sep 17 00:00:00 2001 From: Slava Yurin Date: Wed, 26 Feb 2014 18:38:10 +0700 Subject: Fix inherit listen options for accepted socket Order of options in listen is undocumented but significant. Now user option will replace default value if user set it. --- src/ranch.erl | 27 ++++++++++++++++++--------- test/acceptor_SUITE.erl | 17 ++++++++++++++++- test/check_tcp_options.erl | 15 +++++++++++++++ 3 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 test/check_tcp_options.erl diff --git a/src/ranch.erl b/src/ranch.erl index 17d24d2..c7e9727 100644 --- a/src/ranch.erl +++ b/src/ranch.erl @@ -120,18 +120,27 @@ set_protocol_options(Ref, Opts) -> -spec filter_options([{atom(), any()} | {raw, any(), any(), any()}], [atom()], Acc) -> Acc when Acc :: [any()]. -filter_options([], _, Acc) -> - Acc; -filter_options([Opt = {Key, _}|Tail], AllowedKeys, Acc) -> +filter_options(UserOptions, AllowedKeys, DefaultOptions) -> + AllowedOptions = filter_user_options(UserOptions, AllowedKeys), + lists:foldl(fun merge_options/2, DefaultOptions, AllowedOptions). + +filter_user_options([Opt = {Key, _}|Tail], AllowedKeys) -> case lists:member(Key, AllowedKeys) of - true -> filter_options(Tail, AllowedKeys, [Opt|Acc]); - false -> filter_options(Tail, AllowedKeys, Acc) + true -> [Opt|filter_user_options(Tail, AllowedKeys)]; + false -> filter_user_options(Tail, AllowedKeys) end; -filter_options([Opt = {raw, _, _, _}|Tail], AllowedKeys, Acc) -> +filter_user_options([Opt = {raw, _, _, _}|Tail], AllowedKeys) -> case lists:member(raw, AllowedKeys) of - true -> filter_options(Tail, AllowedKeys, [Opt|Acc]); - false -> filter_options(Tail, AllowedKeys, Acc) - end. + true -> [Opt|filter_user_options(Tail, AllowedKeys)]; + false -> filter_user_options(Tail, AllowedKeys) + end; +filter_user_options([], _) -> + []. + +merge_options({Key, _} = Option, OptionList) -> + lists:keystore(Key, 1, OptionList, Option); +merge_options(Option, OptionList) -> + [Option|OptionList]. -spec set_option_default(Opts, atom(), any()) -> Opts when Opts :: [{atom(), any()}]. diff --git a/test/acceptor_SUITE.erl b/test/acceptor_SUITE.erl index f4789c8..6325055 100644 --- a/test/acceptor_SUITE.erl +++ b/test/acceptor_SUITE.erl @@ -37,6 +37,7 @@ -export([tcp_accept_socket/1]). -export([tcp_active_echo/1]). -export([tcp_echo/1]). +-export([tcp_inherit_options/1]). -export([tcp_max_connections/1]). -export([tcp_max_connections_and_beyond/1]). -export([tcp_set_max_connections/1]). @@ -66,7 +67,8 @@ groups() -> tcp_max_connections_and_beyond, tcp_set_max_connections, tcp_clean_set_max_connections, - tcp_upgrade + tcp_upgrade, + tcp_inherit_options ]}, {ssl, [ ssl_accept_error, ssl_accept_socket, @@ -370,6 +372,19 @@ tcp_upgrade(_) -> receive upgraded -> ok after 1000 -> error(timeout) end, ranch:stop_listener(Name). +tcp_inherit_options(_) -> + Name = tcp_inherit_options, + TcpOptions = [{nodelay, false}, {send_timeout_close, false}], + {ok, _} = ranch:start_listener(Name, 4, ranch_tcp, + [{port, 0} | TcpOptions], + check_tcp_options, [{pid, self()} | TcpOptions]), + 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), + ranch:stop_listener(Name). + %% Supervisor tests supervisor_clean_restart(_) -> diff --git a/test/check_tcp_options.erl b/test/check_tcp_options.erl new file mode 100644 index 0000000..18432ac --- /dev/null +++ b/test/check_tcp_options.erl @@ -0,0 +1,15 @@ +-module(check_tcp_options). +-behaviour(ranch_protocol). + +-export([start_link/4]). +-export([init/3]). + +start_link(_, Socket, _, [{pid, TestPid}|TcpOptions]) -> + {ok, RealTcpOptions} = + inet:getopts(Socket, [Key || {Key, _} <- TcpOptions]), + Pid = spawn_link(?MODULE, init, [TestPid, RealTcpOptions, TcpOptions]), + {ok, Pid}. + +init(Pid, TcpOptions, TcpOptions) -> + Pid ! checked, + receive after 2500 -> ok end. -- cgit v1.2.3