From 1dd926971410c5dd5252347dbbd0831681311522 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sat, 25 Jan 2020 23:08:49 +0100 Subject: Rework/simplify examples Make each example stand on its own by removing the common node module that just made for more difficult reading, and redo the fairly confusing transport config: now it's just a list of transport options as passed to diameter:add_transport/2, along with some syntactic sugar for transport_module/transport_config pairs. Also add a healthy README, that should probably be merged into the documentation. --- lib/diameter/examples/code/GNUmakefile | 4 +- lib/diameter/examples/code/README | 105 ++++++++++++++++ lib/diameter/examples/code/client.erl | 171 +++++++++++++++++--------- lib/diameter/examples/code/client_cb.erl | 29 ++--- lib/diameter/examples/code/node.erl | 202 ------------------------------- lib/diameter/examples/code/relay.erl | 73 ++++++----- lib/diameter/examples/code/relay_cb.erl | 55 +++++---- lib/diameter/examples/code/server.erl | 121 +++++++++++++----- lib/diameter/examples/code/server_cb.erl | 101 +++++++++------- lib/diameter/src/modules.mk | 3 +- 10 files changed, 457 insertions(+), 407 deletions(-) create mode 100644 lib/diameter/examples/code/README delete mode 100644 lib/diameter/examples/code/node.erl diff --git a/lib/diameter/examples/code/GNUmakefile b/lib/diameter/examples/code/GNUmakefile index f5c2e5f869..c0be5b59f4 100644 --- a/lib/diameter/examples/code/GNUmakefile +++ b/lib/diameter/examples/code/GNUmakefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2010-2015. All Rights Reserved. +# Copyright Ericsson AB 2010-2020. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ EXAMPLES = client server relay # redirect proxy CALLBACKS = $(EXAMPLES:%=%_cb) -MODULES = node $(EXAMPLES) $(EXAMPLES:%=%_cb) +MODULES = $(EXAMPLES) $(EXAMPLES:%=%_cb) BEAM = $(MODULES:%=%.beam) diff --git a/lib/diameter/examples/code/README b/lib/diameter/examples/code/README new file mode 100644 index 0000000000..b639849390 --- /dev/null +++ b/lib/diameter/examples/code/README @@ -0,0 +1,105 @@ + +This directory contains small examples of simple Diameter nodes. They +don't do everything a real node should do obviously, but they're a +starting point. + +Each example consists of an interface module with functions to start +and stop a service and add transport, and a corresponding callback +module for the Diameter application the service configures. A real +node might support multiple Diameter applications, either with the +same callback or sharing a common callback, maybe using extra +arguments to distinguish between callbacks for the different +applications. + +The interface functions are named start, stop, connect, and listen; +the client example also has a call function that sends an example +message. Service names should be atoms in these modules (since the +default setting of Origin-Host assumes this), but doesn't need to be +in general. Options are passed directly to diameter:start_service/2 +and diameter:add_transport/2, with some additional convenience options +for the latter; in particular, the atoms tcp and sctp to connect to or +listen on default endpoints (127.0.01:3868), or tuples with protocol +and another endpoint {eg. {tcp, {192,168,1,5}, 3869}. This convenience +makes the simplest usage like this in an Erlang shell: + + diameter:start(). + server:start(). + server:listen(tcp). + client:start(). + client:connect(tcp). + client:call(). + +Or put a relay between the client and server: + + diameter:start(). + server:start(). + server:listen(sctp). + relay:start(). + relay:connect(sctp). + relay:listen(tcp). + client:start(). + client:connect(tcp). + client:call(). + +Most services should probably set the following options, which have +been added to solve various problems over the years, while the +defaults have not been changed for backwards compatibility. + + {decode_format, map} + + Provide decoded messages in #diameter_packet.msg of a + handle_request or handle_answer callback in the form [Name | Avps], + where Name is the atom() name of the message in the (Diameter) + application dictionary in question (eg. 'ACR') and Avps is a map + of AVP values. This avoids compile-time dependencies on the + generated records and their (generally) long-winded names. The + hrl files generated from dictionaries are best avoided. + + {restrict_connections, false} + + Accept multiple connections with the same peer. By default, + diameter will only accept a single connection with a given peer, + which the Diameter RFC can be interpreted as requiring. In + practice, wanting multiple connections to the same peer is + common. + + {string_decode, false} + + Disable the decoding of string-ish Diameter types to Erlang + strings, leaving them as binary(). Strings can be costly if + decoded Diameter messages are passed between processes. + + {strict_mbit, false} + + Relax the interpretation of the M-bit so that an AVP setting + this bit is not regarded as a 5001 error when the message + grammar in question doesn't explicitly list the AVP. Without + this, a Redirect-Host AVP received in a 3006 + (DIAMETER_REDIRECT_INDICATION) answer-message from a redirect + agent will be treated as error, and there have been other + situations in which the default value has caused problems (or at + least surprise). + + {call_mutates_state, false} (on each configured application) + + Avoid pick_peer and subsequent callbacks going through a single + process, which can be a bottleneck. Better to use the tid() of + an ets table as (immutable) state, for example. + +Other options that are particularly useful/necessary in some +situations are: + + pool_size - Create a pool of accepting processes for a listening + transport, to avoid refused connections when many peers + connect simultaneously. Can also be used on a connecting + transport to establish multiple connections with a + single call to diameter:add_transport/2. + + sequence - Ensure unique End-to-End and Hop-by-Hop identifiers over + a cluster of Erlang nodes. + + share_peers - Share peer connections across a cluster of + use_shared_peers Erlang nodes. + + spawn_opt - Replace diameter's spawning of a new handler process for + each request by something else: diameter_dist is an example. diff --git a/lib/diameter/examples/code/client.erl b/lib/diameter/examples/code/client.erl index 0864919cdd..04c91e37fa 100644 --- a/lib/diameter/examples/code/client.erl +++ b/lib/diameter/examples/code/client.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2017. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -18,120 +18,173 @@ %% %CopyrightEnd% %% +-module(client). + %% -%% An example Diameter client that can sends base protocol RAR +%% An example Diameter client that can sends base protocol ACR %% requests to a connected peer. %% -%% The simplest usage is as follows this to connect to a server -%% listening on the default port on the local host, assuming diameter -%% is already started (eg. diameter:start()). +%% Simplest usage to connect to a server listening on TCP at +%% 127.0.0.1:3868: %% %% client:start(). %% client:connect(tcp). %% client:call(). %% -%% The first call starts the a service with the default name of -%% ?MODULE, the second defines a connecting transport that results in -%% a connection to the peer (if it's listening), the third sends it a -%% RAR and returns the answer. -%% - --module(client). - --include_lib("diameter/include/diameter.hrl"). -export([start/1, %% start a service start/2, %% connect/2, %% add a connecting transport - call/1, %% send using the record encoding - cast/1, %% send using the list encoding and detached + call/2, %% send a request stop/1]). %% stop a service -%% A real application would typically choose an encoding and whether -%% they want the call to return the answer or not. Sending with -%% both the record and list encoding here, one detached and one not, -%% is just for demonstration purposes. %% Convenience functions using the default service name. -export([start/0, connect/1, stop/0, call/0, - cast/0]). + call/1]). -define(DEF_SVC_NAME, ?MODULE). -define(L, atom_to_list). +-define(LOOPBACK, {127,0,0,1}). -%% The service configuration. As in the server example, a client -%% supporting multiple Diameter applications may or may not want to -%% configure a common callback module on all applications. +%% Service configuration. -define(SERVICE(Name), [{'Origin-Host', ?L(Name) ++ ".example.com"}, {'Origin-Realm', "example.com"}, {'Vendor-Id', 0}, {'Product-Name', "Client"}, {'Auth-Application-Id', [0]}, - {string_decode, false}, {decode_format, map}, + {restrict_connections, false}, + {strict_mbit, false}, + {string_decode, false}, {application, [{alias, common}, {dictionary, diameter_gen_base_rfc6733}, - {module, client_cb}]}]). + {module, client_cb}, + {answer_errors, callback}, + {call_mutates_state, false}]}]). -%% start/1 +%% start/2 -start(Name) - when is_atom(Name) -> - start(Name, []); +start(Name, Opts) -> + Defaults = [T || {K,_} = T <- ?SERVICE(Name), + not lists:keymember(K, 1, Opts)], + diameter:start_service(Name, Opts ++ Defaults). -start(Opts) - when is_list(Opts) -> +%% start/1 + +start(Opts) -> start(?DEF_SVC_NAME, Opts). %% start/0 start() -> - start(?DEF_SVC_NAME). + start(?DEF_SVC_NAME, []). -%% start/2 +%% connect/1 -start(Name, Opts) -> - node:start(Name, Opts ++ [T || {K,_} = T <- ?SERVICE(Name), - false == lists:keymember(K, 1, Opts)]). +connect(Opts) -> + connect(?DEF_SVC_NAME, Opts). %% connect/2 +connect(Name, Opts) + when is_list(Opts) -> + diameter:add_transport(Name, {connect, lists:flatmap(fun opts/1, Opts)}); + +%% backwards compatibility with old config +connect(Name, {T, Opts}) -> + connect(Name, [T | Opts]); connect(Name, T) -> - node:connect(Name, T). + connect(Name, [T]). -connect(T) -> - connect(?DEF_SVC_NAME, T). +%% call/2 -%% call/1 +call(Name, #{'Session-Id' := _} = Avps) -> + Defaults = #{'Destination-Realm' => "example.com", + 'Accounting-Record-Type' => 1, %% EVENT_RECORD + 'Accounting-Record-Number' => 0}, + ACR = ['ACR' | maps:merge(Defaults, Avps)], + diameter:call(Name, common, ACR, []); -call(Name) -> - SId = diameter:session_id(?L(Name)), - RAR = ['RAR' | #{'Session-Id' => SId, - 'Auth-Application-Id' => 0, - 'Re-Auth-Request-Type' => 0}], - diameter:call(Name, common, RAR, []). +call(Name, #{} = Avps) -> + call(Name, Avps#{'Session-Id' => diameter:session_id(?L(Name))}); -call() -> - call(?DEF_SVC_NAME). +call(Name, Avps) -> + call(Name, maps:from_list(Avps)). -%% cast/1 +%% call/1 + +call(Avps) -> + call(?DEF_SVC_NAME, Avps). -cast(Name) -> - SId = diameter:session_id(?L(Name)), - RAR = ['RAR', {'Session-Id', SId}, - {'Auth-Application-Id', 0}, - {'Re-Auth-Request-Type', 1}], - diameter:call(Name, common, RAR, [detach]). +%% call/0 -cast() -> - cast(?DEF_SVC_NAME). +call() -> + call(?DEF_SVC_NAME, #{}). %% stop/1 stop(Name) -> - node:stop(Name). + diameter:stop_service(Name). stop() -> stop(?DEF_SVC_NAME). + +%% =========================================================================== + +%% opts/1 +%% +%% Map some terms to transport_module/transport_config pairs as a +%% convenience, pass everything else unmodified. + +opts(T) + when T == any; + T == tcp; + T == sctp -> + opts({T, loopback, 3868}); + +opts({T, RA, RP}) -> + opts({T, [], RA, RP}); + +opts({T, loopback, RA, RP}) -> + opts({T, ?LOOPBACK, RA, RP}); + +opts({T, LA, RA, RP}) + when is_tuple(LA) -> + opts({T, [{ip, LA}], RA, RP}); + +opts({any, Opts, RA, RP}) -> + All = Opts ++ opts(RA, RP), + [{transport_module, diameter_sctp}, + {transport_config, All, 2000}, + {transport_module, diameter_tcp}, + {transport_config, All}]; + +opts({tcp, Opts, RA, RP}) -> + opts({diameter_tcp, Opts, RA, RP}); + +opts({sctp, Opts, RA, RP}) -> + opts({diameter_sctp, Opts, RA, RP}); + +opts({Mod, Opts, loopback, RP}) -> + opts({Mod, Opts, ?LOOPBACK, RP}); + +opts({Mod, Opts, RA, default}) -> + opts({Mod, Opts, RA, 3868}); + +opts({Mod, Opts, RA, RP}) -> + [{transport_module, Mod}, + {transport_config, opts(RA, RP) ++ Opts}]; + +opts(T) -> + [T]. + +%% opts/2 + +opts(loopback, RP) -> + opts(?LOOPBACK, RP); + +opts(RA, RP) -> + [{raddr, RA}, {rport, RP}, {reuseaddr, true}]. diff --git a/lib/diameter/examples/code/client_cb.erl b/lib/diameter/examples/code/client_cb.erl index af2d4d6da7..7721c47a9a 100644 --- a/lib/diameter/examples/code/client_cb.erl +++ b/lib/diameter/examples/code/client_cb.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2017. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -21,7 +21,6 @@ -module(client_cb). -include_lib("diameter/include/diameter.hrl"). --include_lib("diameter/include/diameter_gen_base_rfc3588.hrl"). %% diameter callbacks -export([peer_up/3, @@ -50,28 +49,16 @@ pick_peer([Peer | _], _, _SvcName, _State) -> %% prepare_request/3 -prepare_request(#diameter_packet{msg = ['RAR' = T | Avps]}, _, {_, Caps}) -> - #diameter_caps{origin_host = {OH, DH}, - origin_realm = {OR, DR}} +prepare_request(#diameter_packet{msg = [Name | Avps]}, _, {_, Caps}) -> + #diameter_caps{origin_host = {OH, _}, + origin_realm = {OR, _}} = Caps, - - {send, [T | if is_map(Avps) -> - Avps#{'Origin-Host' => OH, - 'Origin-Realm' => OR, - 'Destination-Host' => DH, - 'Destination-Realm' => DR}; - is_list(Avps) -> - [{'Origin-Host', OH}, - {'Origin-Realm', OR}, - {'Destination-Host', DH}, - {'Destination-Realm', DR} - | Avps] - end]}. + {send, [Name | Avps#{'Origin-Host' => OH, 'Origin-Realm' => OR}]}. %% prepare_retransmit/3 -prepare_retransmit(Packet, SvcName, Peer) -> - prepare_request(Packet, SvcName, Peer). +prepare_retransmit(Pkt, _SvcName, _Peer) -> + {send, Pkt}. %% handle_answer/4 @@ -86,4 +73,4 @@ handle_error(Reason, _Request, _SvcName, _Peer) -> %% handle_request/3 handle_request(_Packet, _SvcName, _Peer) -> - erlang:error({unexpected, ?MODULE, ?LINE}). + {answer_message, 3001}. %% DIAMETER_COMMAND_UNSUPPORTED diff --git a/lib/diameter/examples/code/node.erl b/lib/diameter/examples/code/node.erl deleted file mode 100644 index 77810bf893..0000000000 --- a/lib/diameter/examples/code/node.erl +++ /dev/null @@ -1,202 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2010-2018. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - -%% -%% A library module used by the example Diameter nodes. Does little -%% more than provide an alternate/simplified transport configuration. -%% - --module(node). - --export([start/2, - listen/2, - connect/2, - stop/1]). - --export([message/3]). - --type protocol() - :: tcp | sctp. - --type ip_address() - :: default - | inet:ip_address(). - --type server_transport() - :: protocol() - | {protocol(), ip_address(), non_neg_integer()}. - --type server_opts() - :: server_transport() - | {server_transport(), [diameter:transport_opt()]} - | [diameter:transport_opt()]. - --type client_transport() - :: protocol() | any - | {protocol() | any, ip_address(), non_neg_integer()} - | {protocol() | any, ip_address(), ip_address(), non_neg_integer()}. - --type client_opts() - :: client_transport() - | {client_transport(), [diameter:transport_opt()]} - | [diameter:transport_opt()]. - -%% The server_transport() and client_transport() config is just -%% convenience: arbitrary options can be specifed as a -%% [diameter:transport_opt()]. - --define(DEFAULT_PORT, 3868). - -%% --------------------------------------------------------------------------- -%% Interface functions -%% --------------------------------------------------------------------------- - -%% start/2 - --spec start(diameter:service_name(), [diameter:service_opt()]) - -> ok - | {error, term()}. - -start(Name, Opts) - when is_atom(Name), is_list(Opts) -> - diameter:start_service(Name, Opts). - -%% connect/2 - --spec connect(diameter:service_name(), client_opts()) - -> {ok, diameter:transport_ref()} - | {error, term()}. - -connect(Name, Opts) - when is_list(Opts) -> - diameter:add_transport(Name, {connect, Opts}); - -connect(Name, {T, Opts}) -> - connect(Name, Opts ++ client_opts(T)); - -connect(Name, T) -> - connect(Name, [{connect_timer, 5000} | client_opts(T)]). - -%% listen/2 - --spec listen(diameter:service_name(), server_opts()) - -> {ok, diameter:transport_ref()} - | {error, term()}. - -listen(Name, Opts) - when is_list(Opts) -> - diameter:add_transport(Name, {listen, Opts}); - -listen(Name, {T, Opts}) -> - listen(Name, Opts ++ server_opts(T)); - -listen(Name, T) -> - listen(Name, server_opts(T)). - -%% stop/1 - --spec stop(diameter:service_name()) - -> ok - | {error, term()}. - -stop(Name) -> - diameter:stop_service(Name). - -%% --------------------------------------------------------------------------- -%% Internal functions -%% --------------------------------------------------------------------------- - -%% server_opts/1 -%% -%% Return transport options for a listening transport. - -server_opts({T, Addr, Port}) -> - [{transport_module, tmod(T)}, - {transport_config, [{reuseaddr, true}, - {sender, true}, - {message_cb, [fun ?MODULE:message/3, 0]}, - {ip, addr(Addr)}, - {port, Port}]}]; - -server_opts(T) -> - server_opts({T, loopback, ?DEFAULT_PORT}). - -%% client_opts/1 -%% -%% Return transport options for a connecting transport. - -client_opts({T, LA, RA, RP}) - when T == all; %% backwards compatibility - T == any -> - [[S, {C,Os}], T] = [client_opts({P, LA, RA, RP}) || P <- [sctp,tcp]], - [S, {C,Os,2000} | T]; - -client_opts({T, LA, RA, RP}) -> - [{transport_module, tmod(T)}, - {transport_config, [{raddr, addr(RA)}, - {rport, RP}, - {reuseaddr, true} - | ip(LA)]}]; - -client_opts({T, RA, RP}) -> - client_opts({T, default, RA, RP}); - -client_opts(T) -> - client_opts({T, loopback, loopback, ?DEFAULT_PORT}). - -%% --------------------------------------------------------------------------- - -tmod(tcp) -> diameter_tcp; -tmod(sctp) -> diameter_sctp. - -ip(default) -> - []; -ip(loopback) -> - [{ip, {127,0,0,1}}]; -ip(Addr) -> - [{ip, Addr}]. - -addr(loopback) -> - {127,0,0,1}; -addr(A) -> - A. - -%% --------------------------------------------------------------------------- - -%% message/3 -%% -%% Simple message callback that limits the number of concurrent -%% requests on the peer connection in question. - -%% Incoming request. -message(recv, <<_:32, 1:1, _/bits>> = Bin, N) -> - [Bin, N < 32, fun ?MODULE:message/3, N+1]; - -%% Outgoing request. -message(ack, <<_:32, 1:1, _/bits>>, _) -> - []; - -%% Incoming answer or request discarded. -message(ack, _, N) -> - [N =< 32, fun ?MODULE:message/3, N-1]; - -%% Outgoing message or incoming answer. -message(_, Bin, _) -> - [Bin]. diff --git a/lib/diameter/examples/code/relay.erl b/lib/diameter/examples/code/relay.erl index 806f79915b..ec53ac01f1 100644 --- a/lib/diameter/examples/code/relay.erl +++ b/lib/diameter/examples/code/relay.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2016. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -18,81 +18,94 @@ %% %CopyrightEnd% %% +-module(relay). + %% %% An example Diameter relay agent. %% -%% Usage to connect to a server listening on the default port over TCP -%% and to listen on the default port over SCTP is as follows, assuming -%% diameter is already started (eg. diameter:start()). +%% Simplets usage to connect to a server listening on TCP at +%% 127.0.0.1:3868 and listen for connections on TCP at the same +%% endpoint: %% -%% Eg. relay:start(). -%% relay:connect(tcp). -%% relay:listen(sctp). +%% relay:start(). +%% relay:connect(tcp). +%% relay:listen(sctp). %% --module(relay). - +%% Interface. -export([start/1, start/2, listen/2, connect/2, stop/1]). +%% Convenience functions using the default service name. -export([start/0, listen/1, connect/1, stop/0]). +%% Default service name. -define(DEF_SVC_NAME, ?MODULE). -%% The service configuration. +%% Service configuration. -define(SERVICE(Name), [{'Origin-Host', atom_to_list(Name) ++ ".example.com"}, {'Origin-Realm', "example.com"}, {'Vendor-Id', 193}, {'Product-Name', "RelayAgent"}, {'Auth-Application-Id', [16#FFFFFFFF]}, + {decode_format, map}, + {restrict_connections, false}, {string_decode, false}, + {strict_mbit, false}, {application, [{alias, relay}, {dictionary, diameter_gen_relay}, - {module, relay_cb}]}]). + {module, relay_cb}, + {call_mutates_state, false}]}]). -%% start/1 +%% start/2 -start(Name) - when is_atom(Name) -> - start(Name, []). +start(Name, Opts) -> + Defaults = [T || {K,_} = T <- ?SERVICE(Name), + not lists:keymember(K, 1, Opts)], + diameter:start_service(Name, Opts ++ Defaults). %% start/1 -start() -> - start(?DEF_SVC_NAME). +start(Opts) -> + start(?DEF_SVC_NAME, Opts). -%% start/2 +%% start/0 -start(Name, Opts) -> - node:start(Name, Opts ++ [T || {K,_} = T <- ?SERVICE(Name), - false == lists:keymember(K, 1, Opts)]). +start() -> + start(?DEF_SVC_NAME, []). %% listen/2 -listen(Name, T) -> - node:listen(Name, T). +listen(Name, Opts) -> + server:listen(Name, Opts). -listen(T) -> - listen(?DEF_SVC_NAME, T). +%% listen/1 + +listen(Opts) -> + listen(?DEF_SVC_NAME, Opts). %% connect/2 -connect(Name, T) -> - node:connect(Name, T). +connect(Name, Opts) -> + client:connect(Name, Opts). + +%% connect/1 -connect(T) -> - connect(?DEF_SVC_NAME, T). +connect(Opts) -> + connect(?DEF_SVC_NAME, Opts). %% stop/1 stop(Name) -> - node:stop(Name). + diameter:stop_service(Name). + +%% stop/0 stop() -> stop(?DEF_SVC_NAME). diff --git a/lib/diameter/examples/code/relay_cb.erl b/lib/diameter/examples/code/relay_cb.erl index 6df1738143..0d56a14401 100644 --- a/lib/diameter/examples/code/relay_cb.erl +++ b/lib/diameter/examples/code/relay_cb.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2016. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -21,48 +21,59 @@ -module(relay_cb). -include_lib("diameter/include/diameter.hrl"). --include_lib("diameter/include/diameter_gen_base_rfc3588.hrl"). %% diameter callbacks -export([peer_up/3, peer_down/3, - pick_peer/5, - prepare_request/4, - prepare_retransmit/4, - handle_answer/5, - handle_error/5, + pick_peer/4, + prepare_request/3, + prepare_retransmit/3, + handle_answer/4, + handle_error/4, handle_request/3]). +%% peer_up/3 + peer_up(_SvcName, _Peer, State) -> State. +%% peer_down/3 + peer_down(_SvcName, _Peer, State) -> State. -%% Returning 'relay' from handle_request causes diameter to resend the -%% incoming request, which leads to pick_peer and prepare_request -%% callbacks as if sending explicitly. The 'extra' argument is -%% appended to the argument list for callbacks following from -%% resending of the request. +%% handle_request/3 + +%% Assume the destination is directly connected; filter +%% correspondingly; don't relay to the sender. +handle_request(_Pkt, _SvcName, {_, Caps}) -> + #diameter_caps{origin_host = {_, OH}} + = Caps, + {relay, [{timeout, 2000}, + {filter, {all, [host, realm, {neg, {host, OH}}]}}]}. -handle_request(_Pkt, _SvcName, _Peer) -> - {relay, [{timeout, 1000}, {extra, [relayed]}]}. +%% pick_peer/4 -%% diameter will filter the sender in the Peers list. -pick_peer([Peer | _], _, _SvcName, _State, relayed) -> +pick_peer([Peer | _], _, _SvcName, _State) -> {ok, Peer}. -prepare_request(Pkt, _SvcName, _Peer, relayed) -> +%% prepare_request/3 + +prepare_request(Pkt, _SvcName, _Peer) -> {send, Pkt}. -prepare_retransmit(Pkt, _SvcName, _Peer, relayed) -> +%% prepare_request/3 + +prepare_retransmit(Pkt, _SvcName, _Peer) -> {send, Pkt}. -%% diameter expects handle_answer to return the diameter_packet record -%% containing the answer when called for a relayed request. +%% handle_answer/4 -handle_answer(Pkt, _Request, _SvcName, _Peer, relayed) -> +%% Relay an answer by returning the first argument. +handle_answer(Pkt, _Request, _SvcName, _Peer) -> Pkt. -handle_error(Reason, _Request, _SvcName, _Peer, relayed) -> +%% handle_error/4 + +handle_error(Reason, _Request, _SvcName, _Peer) -> {error, Reason}. diff --git a/lib/diameter/examples/code/server.erl b/lib/diameter/examples/code/server.erl index a91be70664..6ee49fb678 100644 --- a/lib/diameter/examples/code/server.erl +++ b/lib/diameter/examples/code/server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2015. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -18,23 +18,20 @@ %% %CopyrightEnd% %% +-module(server). + %% -%% An example Diameter server that can respond to the base protocol -%% RAR sent by the client example. +%% An example Diameter server that answers the base protocol ACR sent +%% by the client example. %% -%% The simplest example to start a server listening on the loopback -%% address (which will serve the example usage given in client.erl) is -%% like this assuming diameter is already started (eg. diameter:start()): +%% Simplest usage to listen on TCP at 127.0.0.1:3868: %% %% server:start(). %% server:listen(tcp). %% -%% The first call starts a service, the second adds a transport listening -%% on the default port. -%% --module(server). +%% Interface. -export([start/1, %% start a service start/2, %% listen/2, %% add a listening transport @@ -45,6 +42,9 @@ listen/1, stop/0]). +%% Internal callback. +-export([message/3]). + -define(DEF_SVC_NAME, ?MODULE). %% The service configuration. In a server supporting multiple Diameter @@ -55,45 +55,110 @@ {'Vendor-Id', 193}, {'Product-Name', "Server"}, {'Auth-Application-Id', [0]}, + {decode_format, map}, {restrict_connections, false}, + {strict_mbit, false}, {string_decode, false}, {application, [{alias, common}, {dictionary, diameter_gen_base_rfc6733}, - {module, server_cb}]}]). + {module, server_cb}, + {call_mutates_state, false}]}]). -%% start/1 +%% start/2 -start(Name) - when is_atom(Name) -> - start(Name, []); +start(Name, Opts) -> + Defaults = [T || {K,_} = T <- ?SERVICE(Name), + not lists:keymember(K, 1, Opts)], + diameter:start_service(Name, Opts ++ Defaults). -start(Opts) - when is_list(Opts) -> +%% start/1 + +start(Opts) -> start(?DEF_SVC_NAME, Opts). %% start/0 start() -> - start(?DEF_SVC_NAME). - -%% start/2 - -start(Name, Opts) -> - node:start(Name, Opts ++ [T || {K,_} = T <- ?SERVICE(Name), - false == lists:keymember(K, 1, Opts)]). + start(?DEF_SVC_NAME, []). %% listen/2 +listen(Name, Opts) + when is_list(Opts) -> + diameter:add_transport(Name, {listen, lists:flatmap(fun opts/1, Opts)}); + +%% backwards compatibility with old config +listen(Name, {T, Opts}) -> + listen(Name, [T | Opts]); listen(Name, T) -> - node:listen(Name, T). + listen(Name, [T]). -listen(T) -> - listen(?DEF_SVC_NAME, T). +%% listen/1 + +listen(Opts) -> + listen(?DEF_SVC_NAME, Opts). %% stop/1 stop(Name) -> - node:stop(Name). + diameter:stop_service(Name). + +%% stop/0 stop() -> stop(?DEF_SVC_NAME). + +%% =========================================================================== + +%% opts/1 +%% +%% Map a 3-tuple a transport_module/transport_config pair as a +%% convenience, pass everything else unmodified. + +opts(T) + when T == tcp; + T == sctp -> + opts({T, loopback, default}); + +opts({tcp, Addr, Port}) -> + opts({diameter_tcp, Addr, Port}); + +opts({sctp, Addr, Port}) -> + opts({diameter_sctp, Addr, Port}); + +opts({Mod, loopback, Port}) -> + opts({Mod, {127,0,0,1}, Port}); + +opts({Mod, Addr, default}) -> + opts({Mod, Addr, 3868}); + +opts({Mod, Addr, Port}) -> + [{transport_module, Mod}, + {transport_config, [{reuseaddr, true}, + {sender, true}, + {message_cb, {?MODULE, message, [0]}}, + {ip, Addr}, + {port, Port}]}]; +opts(T) -> + [T]. + +%% message/3 +%% +%% Simple message callback that limits the number of concurrent +%% requests on the peer connection in question. + +%% Incoming request. +message(recv, <<_:32, 1:1, _/bits>> = Bin, N) -> + [Bin, N < 32, {?MODULE, message, [N+1]}]; + +%% Outgoing request. +message(ack, <<_:32, 1:1, _/bits>>, _) -> + []; + +%% Incoming answer or request discarded. +message(ack, _, N) -> + [N =< 32, {?MODULE, message, [N-1]}]; + +%% Outgoing message or incoming answer. +message(_, Bin, _) -> + [Bin]. diff --git a/lib/diameter/examples/code/server_cb.erl b/lib/diameter/examples/code/server_cb.erl index a2fb8fbda6..5662717c00 100644 --- a/lib/diameter/examples/code/server_cb.erl +++ b/lib/diameter/examples/code/server_cb.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2015. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -25,7 +25,6 @@ -module(server_cb). -include_lib("diameter/include/diameter.hrl"). --include_lib("diameter/include/diameter_gen_base_rfc6733.hrl"). %% diameter callbacks -export([peer_up/3, @@ -37,67 +36,87 @@ handle_error/4, handle_request/3]). --define(UNEXPECTED, erlang:error({unexpected, ?MODULE, ?LINE})). +%% Raise an error on callbacks that aren't expected. +-define(ERROR, error({unexpected, ?MODULE, ?LINE})). + +%% peer_up/3 peer_up(_SvcName, _Peer, State) -> State. +%% peer_down/3 + peer_down(_SvcName, _Peer, State) -> State. -pick_peer(_, _, _SvcName, _State) -> - ?UNEXPECTED. +%% pick_peer/3 + +%% Don't let requests be sent, so other request callbacks shouldn't +%% happen. +pick_peer(_LocalCandidates, _RemoteCandidates, _SvcName, _State) -> + false. + +%% prepare_request/3 -prepare_request(_, _SvcName, _Peer) -> - ?UNEXPECTED. +prepare_request(_Packet, _SvcName, _Peer) -> + ?ERROR. + +%% prepare_retransmit/3 prepare_retransmit(_Packet, _SvcName, _Peer) -> - ?UNEXPECTED. + ?ERROR. + +%% handle_answer/4 handle_answer(_Packet, _Request, _SvcName, _Peer) -> - ?UNEXPECTED. + ?ERROR. + +%% handle_error/4 handle_error(_Reason, _Request, _SvcName, _Peer) -> - ?UNEXPECTED. + ?ERROR. + +%% handle_request/3 -%% A request whose decode was successful ... -handle_request(#diameter_packet{msg = Req, errors = []}, _SvcName, {_, Caps}) - when is_record(Req, diameter_base_RAR) -> +%% ACR without decode errors. +handle_request(#diameter_packet{msg = ['ACR' | #{} = Request], + errors = []}, + _SvcName, + {_, Caps}) -> #diameter_caps{origin_host = {OH,_}, origin_realm = {OR,_}} = Caps, - #diameter_base_RAR{'Session-Id' = Id, - 'Re-Auth-Request-Type' = Type} - = Req, - - {reply, #diameter_base_RAA{'Result-Code' = rc(Type), - 'Origin-Host' = OH, - 'Origin-Realm' = OR, - 'Session-Id' = Id}}; - -%% ... or one that wasn't. 3xxx errors are answered by diameter itself -%% but these are 5xxx errors for which we must contruct a reply. -%% diameter will set Result-Code and Failed-AVP's. -handle_request(#diameter_packet{msg = Req}, _SvcName, {_, Caps}) - when is_record(Req, diameter_base_RAR) -> + + #{'Session-Id' := Sid, + 'Accounting-Record-Type' := T, + 'Accounting-Record-Number' := N} + = Request, + + Answer = #{'Result-Code' => 2001, %% DIAMETER_SUCCESS + 'Origin-Host' => OH, + 'Origin-Realm' => OR, + 'Session-Id' => Sid, + 'Accounting-Record-Type' => T, + 'Accounting-Record-Number' => N}, + + {reply, ['ACA' | Answer]}; + +%% ACR with decode errors. +handle_request(#diameter_packet{msg = ['ACR' | #{} = Request]}, + _SvcName, + {_, Caps}) -> #diameter_caps{origin_host = {OH,_}, origin_realm = {OR,_}} = Caps, - #diameter_base_RAR{'Session-Id' = Id} - = Req, - {reply, #diameter_base_RAA{'Origin-Host' = OH, - 'Origin-Realm' = OR, - 'Session-Id' = Id}}; + Answer = maps:merge(maps:with(['Session-Id'], Request), + #{'Origin-Host' => OH, + 'Origin-Realm' => OR}), -%% Answer that any other message is unsupported. + %% Let diameter set Result-Code and Failed-AVP if there were + %% decode errors. + {reply, ['answer-message' | Answer]}; + +%% Answer anything else as unsupported. handle_request(#diameter_packet{}, _SvcName, _) -> {answer_message, 3001}. %% DIAMETER_COMMAND_UNSUPPORTED - -%% Map Re-Auth-Request-Type to Result-Code just for the purpose of -%% generating different answers. - -rc(0) -> - 2001; %% DIAMETER_SUCCESS -rc(_) -> - 5012. %% DIAMETER_UNABLE_TO_COMPLY diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk index d16292bb88..144d08fcba 100644 --- a/lib/diameter/src/modules.mk +++ b/lib/diameter/src/modules.mk @@ -1,7 +1,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2010-2019. All Rights Reserved. +# Copyright Ericsson AB 2010-2020. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -98,7 +98,6 @@ BINS = \ # Released files relative to ../examples. EXAMPLES = \ code/GNUmakefile \ - code/node.erl \ code/client.erl \ code/client_cb.erl \ code/server.erl \ -- cgit v1.2.1 From 3068ec1700dca6eb60aa25fe4cb22d895fa6a427 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sun, 26 Jan 2020 11:22:09 +0100 Subject: Finish/fix redirect example The code was both broken and incomplete. Now it answers as it should, albeit using some encode functionality (ie. 3-tuple as diameter_avp value) that isn't currently documented. --- lib/diameter/examples/code/GNUmakefile | 2 +- lib/diameter/examples/code/redirect.erl | 66 +++++++++++++++++++++--------- lib/diameter/examples/code/redirect_cb.erl | 62 +++++++++++++++++++++++----- lib/diameter/src/modules.mk | 2 + 4 files changed, 101 insertions(+), 31 deletions(-) diff --git a/lib/diameter/examples/code/GNUmakefile b/lib/diameter/examples/code/GNUmakefile index c0be5b59f4..9efb966e65 100644 --- a/lib/diameter/examples/code/GNUmakefile +++ b/lib/diameter/examples/code/GNUmakefile @@ -18,7 +18,7 @@ # %CopyrightEnd% # -EXAMPLES = client server relay # redirect proxy +EXAMPLES = client server relay redirect # proxy CALLBACKS = $(EXAMPLES:%=%_cb) MODULES = $(EXAMPLES) $(EXAMPLES:%=%_cb) diff --git a/lib/diameter/examples/code/redirect.erl b/lib/diameter/examples/code/redirect.erl index 6934e54507..1f9749ba84 100644 --- a/lib/diameter/examples/code/redirect.erl +++ b/lib/diameter/examples/code/redirect.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2016. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -20,52 +20,80 @@ -module(redirect). --include_lib("diameter/include/diameter.hrl"). --include_lib("diameter/include/diameter_gen_base_rfc3588.hrl"). +%% +%% An example Diameter redirect agent. +%% +%% Simplest usage to listen on TCP at 127.0.0.1:3868. +%% +%% redirect:start(). +%% redirect:listen(tcp). +%% +%% Interface. -export([start/1, listen/2, stop/1]). +%% Convenience functions using the default service name. -export([start/0, listen/1, stop/0]). --define(APP_ALIAS, ?MODULE). --define(SVC_NAME, ?MODULE). --define(CALLBACK_MOD, redirect_mod). +-define(DEF_SVC_NAME, ?MODULE). %% The service configuration. -define(SERVICE(Name), [{'Origin-Host', atom_to_list(Name) ++ ".example.com"}, {'Origin-Realm', "example.com"}, {'Vendor-Id', 193}, {'Product-Name', "RedirectAgent"}, - {'Auth-Application-Id', [?DIAMETER_APP_ID_RELAY]}, - {application, [{alias, ?APP_ALIAS}, - {dictionary, ?DIAMETER_DICT_RELAY}, - {module, ?CALLBACK_MOD}]}]). + {'Auth-Application-Id', [16#FFFFFFFF]}, + {decode_format, map}, + {restrict_connections, false}, + {strict_mbit, false}, + {string_decode, false}, + {application, [{alias, redirect}, + {dictionary, diameter_gen_relay}, + {module, redirect_cb}, + {call_mutates_state, false}]}]). + +%% start/0 + +start() -> + start(?DEF_SVC_NAME). %% start/1 start(Name) when is_atom(Name) -> - peer:start(Name, ?SERVICE(Name)). + start(Name, []); -start() -> - start(?SVC_NAME). +start(Opts) + when is_list(Opts) -> + start(?DEF_SVC_NAME, Opts). + +%% start/2 + +start(Name, Opts) -> + Defaults = [T || {K,_} = T <- ?SERVICE(Name), + not lists:keymember(K, 1, Opts)], + diameter:start_service(Name, Opts ++ Defaults). %% listen/2 -listen(Name, T) -> - peer:listen(Name, T). +listen(Name, Opts) -> + server:listen(Name, Opts). + +%% listen/1 -listen(T) -> - listen(?SVC_NAME, T). +listen(Opts) -> + listen(?DEF_SVC_NAME, Opts). %% stop/1 stop(Name) -> - peer:stop(Name). + diameter:stop_service(Name). + +%% stop.0 stop() -> - stop(?SVC_NAME). + stop(?DEF_SVC_NAME). diff --git a/lib/diameter/examples/code/redirect_cb.erl b/lib/diameter/examples/code/redirect_cb.erl index 8325e86391..76ab091be3 100644 --- a/lib/diameter/examples/code/redirect_cb.erl +++ b/lib/diameter/examples/code/redirect_cb.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2016. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -21,7 +21,6 @@ -module(redirect_cb). -include_lib("diameter/include/diameter.hrl"). --include_lib("diameter/include/diameter_gen_base_rfc3588.hrl"). %% diameter callbacks -export([peer_up/3, @@ -33,30 +32,71 @@ handle_error/4, handle_request/3]). --define(UNEXPECTED, erlang:error({unexpected, ?MODULE, ?LINE})). +%% Dictionary to encode answer-message AVPs with. +-define(Dict, diameter_gen_base_rfc6733). + +%% Raise an error on callbacks that aren't expected. +-define(ERROR, error({unexpected, ?MODULE, ?LINE})). + +%% peer_up/3 peer_up(_SvcName, _Peer, State) -> State. +%% peer_down/3 + peer_down(_SvcName, _Peer, State) -> State. +%% pick_peer/4 + pick_peer(_, _, _SvcName, _State) -> - ?UNEXPECTED. + false. + +%% prepare_request/3 prepare_request(_, _SvcName, _Peer) -> - ?UNEXPECTED. + ?ERROR. + +%% prepare_retransmit/3 prepare_retransmit(_Packet, _SvcName, _Peer) -> - ?UNEXPECTED. + ?ERROR. + +%% handle_answer/4 handle_answer(_Packet, _Request, _SvcName, _Peer) -> - ?UNEXPECTED. + ?ERROR. + +%% handle_error/4 handle_error(_Reason, _Request, _SvcName, _Peer) -> - ?UNEXPECTED. + ?ERROR. + +%% handle_request/3 -handle_request(#diameter_packet{msg = _, errors = []}, _SvcName, {_, Caps}) -> - #diameter_caps{} +handle_request(#diameter_packet{avps = Avps}, _, {_, Caps}) -> + #diameter_caps{origin_host = {OH, _}, + origin_realm = {OR, _}} = Caps, - discard. %% TODO + + Tail = [#diameter_avp{data = {?Dict, A, V}} + || {A,V} <- [{'Origin-Host', OH}, + {'Origin-Realm', OR}, + {'Result-Code', 3006}, %% DIAMETER_REDIRECT_INDICATION + {'Redirect-Host', <<"aaa://server.example.com:3868">>}]], + + {reply, ['answer-message' | lists:append([session(Avps), Tail])]}. + +%% =========================================================================== + +%% session/1 + +session(Avps) -> + try + [] = [A || #diameter_avp{code = 263, vendor_id = undefined} = A + <- Avps, + throw(A)] + catch + Avp -> [Avp] + end. diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk index 144d08fcba..cf938785e3 100644 --- a/lib/diameter/src/modules.mk +++ b/lib/diameter/src/modules.mk @@ -104,6 +104,8 @@ EXAMPLES = \ code/server_cb.erl \ code/relay.erl \ code/relay_cb.erl \ + code/redirect.erl \ + code/redirect_cb.erl \ dict/rfc4004_mip.dia \ dict/rfc4005_nas.dia \ dict/rfc4006_cc.dia \ -- cgit v1.2.1 From 68c2f689ececb5f2325d37113c8b7b9076abec5a Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Tue, 11 Feb 2020 21:12:02 +0100 Subject: Don't orphan slave node in example suite The ct_slave start for compilation was orphaned if compilation failed. --- lib/diameter/test/diameter_examples_SUITE.erl | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/lib/diameter/test/diameter_examples_SUITE.erl b/lib/diameter/test/diameter_examples_SUITE.erl index ee44ed8dc9..7d47efd527 100644 --- a/lib/diameter/test/diameter_examples_SUITE.erl +++ b/lib/diameter/test/diameter_examples_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2017. All Rights Reserved. +%% Copyright Ericsson AB 2013-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -27,6 +27,8 @@ -export([suite/0, all/0, groups/0, + init_per_suite/1, + end_per_suite/1, init_per_group/2, end_per_group/2]). @@ -85,6 +87,13 @@ groups() -> [{all, [parallel], [{group, P} || P <- ?PROTS]} | [{P, [], Tc} || P <- ?PROTS]]. +%% Not used, but a convenient place to enable trace. +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + init_per_group(all, Config) -> Config; @@ -216,12 +225,14 @@ make_name(Dict) -> %% Compile example code under examples/code. code(Config) -> - Node = slave(compile, here()), - [] = rpc:call(Node, - ?MODULE, - install, - [proplists:get_value(priv_dir, Config)]), - {ok, Node} = ct_slave:stop(compile). + try + [] = rpc:call(slave(compile, here()), + ?MODULE, + install, + [proplists:get_value(priv_dir, Config)]) + after + {ok, _} = ct_slave:stop(compile) + end. %% Compile on another node since the code path may be modified. install(PrivDir) -> -- cgit v1.2.1 From 4d7003848ba3e2e3bb022773dca10216e3128838 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sun, 26 Jan 2020 11:17:45 +0100 Subject: Fix comment typos/inaccuracies And the formatting of one line of code. --- lib/diameter/src/Makefile | 2 +- lib/diameter/src/base/diameter_codec.erl | 4 ++-- lib/diameter/src/base/diameter_traffic.erl | 13 +++---------- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile index 2d6dcc811e..b421184b2d 100644 --- a/lib/diameter/src/Makefile +++ b/lib/diameter/src/Makefile @@ -230,7 +230,7 @@ dialyze: opt $(PLT) $(patsubst %, $(EBIN)/%.$(EMULATOR), \ $(notdir $(RT_MODULES) $(CT_MODULES) $(INFO_MODULES))) # Omit all but the common dictionary module since these -# (diameter_gen_relay in particular) generate warning depending on how +# (diameter_gen_relay in particular) generate warnings depending on how # much of the included diameter_gen.hrl they use. # ---------------------------------------------------- diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl index 493a6ab1e3..7f6baf666a 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2018. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -208,7 +208,7 @@ values(Avps) -> encode_avps(_, _, [#diameter_avp{} | _] = Avps, Opts) -> encode_avps(Avps, Opts); -%% ... or as a tuple list or record. +%% ... or as a tuple list, map, or record. encode_avps(Mod, MsgName, Values, Opts) -> Mod:encode_avps(MsgName, Values, Opts). diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index 3a85b7c4d7..82d2c5c59a 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -526,7 +526,7 @@ request_cb(noreply, _App, EvalPktFs, EvalFs) -> %% Relay a request to another peer. This is equivalent to doing an %% explicit call/4 with the message in question except that (1) a loop -%% will be detected by examining Route-Record AVP's, (3) a +%% will be detected by examining Route-Record AVP's, (2) a %% Route-Record AVP will be added to the outgoing request and (3) the %% End-to-End Identifier will default to that in the %% #diameter_header{} without the need for an end_to_end_identifier @@ -562,14 +562,7 @@ request_cb(T, App, _, _) -> send_A({reply, Ans}, TPid, App, Dict0, RecvData, Pkt, _Caps, Fs) -> AppDict = App#diameter_app.dictionary, MsgDict = msg_dict(AppDict, Dict0, Ans), - send_answer(Ans, - TPid, - MsgDict, - AppDict, - Dict0, - RecvData, - Pkt, - Fs); + send_answer(Ans, TPid, MsgDict, AppDict, Dict0, RecvData, Pkt, Fs); send_A({call, Opts}, TPid, App, Dict0, RecvData, Pkt, Caps, Fs) -> AppDict = App#diameter_app.dictionary, @@ -673,7 +666,7 @@ is_answer_message(#diameter_packet{msg = Msg}, Dict0) -> is_answer_message([#diameter_header{is_request = R, is_error = E} | _], _) -> E andalso not R; -%% Message sent as a map or tagged avp/value list. +%% Message sent as a map or avp list. is_answer_message([Name | _], _) -> Name == 'answer-message'; -- cgit v1.2.1 From 7160472ff7b8b787aecc6191e8c45b91c5c9f327 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Mon, 10 Feb 2020 14:08:15 +0100 Subject: Add missing module in dialyze target Which resulted in the following warning. Unknown functions: diameter_dict_parser:parse/1 --- lib/diameter/src/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile index b421184b2d..d6854cfd27 100644 --- a/lib/diameter/src/Makefile +++ b/lib/diameter/src/Makefile @@ -228,7 +228,10 @@ dialyze: opt $(PLT) -Wno_improper_lists \ $(EBIN)/diameter_gen_base_rfc3588.$(EMULATOR) \ $(patsubst %, $(EBIN)/%.$(EMULATOR), \ - $(notdir $(RT_MODULES) $(CT_MODULES) $(INFO_MODULES))) + $(notdir $(DICT_YRL) \ + $(RT_MODULES) \ + $(CT_MODULES) \ + $(INFO_MODULES))) # Omit all but the common dictionary module since these # (diameter_gen_relay in particular) generate warnings depending on how # much of the included diameter_gen.hrl they use. -- cgit v1.2.1 From 788ee60c40029ea6f6438aa47ee753d3ade974f4 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Tue, 28 Jan 2020 23:28:53 +0100 Subject: Add missing type spec Which dialyzer hasn't complained about. --- lib/diameter/src/base/diameter_traffic.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index 82d2c5c59a..48372237d5 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -86,6 +86,7 @@ string_decode := boolean(), strict_arities => diameter:strict_arities(), strict_mbit := boolean(), + ordered_encode => boolean(), incoming_maxlen := diameter:message_length()}}). %% Note that incoming_maxlen is currently handled in diameter_peer_fsm, %% so that any message exceeding the maximum is discarded. Retain the -- cgit v1.2.1 From fd72de550f659f36428925745f698cae3b40acdf Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sat, 25 Jan 2020 11:08:58 +0100 Subject: Reorder service/transport options in type specs Alphabetically (mostly), for better readability. --- lib/diameter/src/base/diameter.erl | 42 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl index ee5ff38a42..2982486a10 100644 --- a/lib/diameter/src/base/diameter.erl +++ b/lib/diameter/src/base/diameter.erl @@ -352,45 +352,45 @@ call(SvcName, App, Message) -> %% Options common to both start_service/2 and add_transport/2. -type common_opt() - :: {pool_size, pos_integer()} + :: {avp_dictionaries, [module()]} | {capabilities_cb, eval()} | {capx_timeout, 'Unsigned32'()} - | {strict_capx, boolean()} - | {strict_mbit, boolean()} - | {avp_dictionaries, [module()]} + | {connect_timer, 'Unsigned32'()} | {disconnect_cb, eval()} - | {dpr_timeout, 'Unsigned32'()} | {dpa_timeout, 'Unsigned32'()} + | {dpr_timeout, 'Unsigned32'()} | {incoming_maxlen, message_length()} | {length_errors, exit | handle | discard} - | {connect_timer, 'Unsigned32'()} - | {watchdog_timer, 'Unsigned32'() | {module(), atom(), list()}} + | {pool_size, pos_integer()} + | {spawn_opt, list() | mfa()} + | {strict_capx, boolean()} + | {strict_mbit, boolean()} | {watchdog_config, [{okay|suspect, non_neg_integer()}]} - | {spawn_opt, list() | mfa()}. + | {watchdog_timer, 'Unsigned32'() | {module(), atom(), list()}}. %% Options passed to start_service/2 -type service_opt() :: capability() | {application, [application_opt()]} + | {decode_format, decode_format()} | {restrict_connections, restriction()} | {sequence, sequence() | eval()} | {share_peers, remotes()} - | {decode_format, decode_format()} - | {traffic_counters, boolean()} - | {string_decode, boolean()} | {strict_arities, true | strict_arities()} + | {string_decode, boolean()} + | {traffic_counters, boolean()} | {use_shared_peers, remotes()} | common_opt(). -type application_opt() :: {alias, app_alias()} + | {answer_errors, callback|report|discard} + | {call_mutates_state, boolean()} | {dictionary, module()} | {module, app_module()} - | {state, any()} - | {call_mutates_state, boolean()} - | {answer_errors, callback|report|discard} - | {request_errors, answer_3xxx|answer|callback}. + | {request_errors, answer_3xxx|answer|callback} + | {state, any()}. -type app_alias() :: any(). @@ -408,11 +408,11 @@ call(SvcName, App, Message) -> %% Options passed to add_transport/2 -type transport_opt() - :: {transport_module, atom()} + :: {applications, [app_alias()]} + | {capabilities, [capability()]} | {transport_config, any()} | {transport_config, any(), 'Unsigned32'() | infinity} - | {applications, [app_alias()]} - | {capabilities, [capability()]} + | {transport_module, atom()} | common_opt() | {private, any()}. @@ -431,8 +431,8 @@ call(SvcName, App, Message) -> %% Options passed to call/4 -type call_opt() - :: {extra, list()} + :: detach + | {extra, list()} | {filter, peer_filter()} - | {timeout, 'Unsigned32'()} | {peer, peer_ref()} - | detach. + | {timeout, 'Unsigned32'()}. -- cgit v1.2.1 From 1ec850b17b2420671b2e20c971eed4d7cd1863ff Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sat, 25 Jan 2020 13:26:43 +0100 Subject: Fix incorrect transport options Commit 5f3becad caused transport options differing from those passed to diameter:add_transport/2 to be used in several situations: when starting a transport process after connect_timer expiry after an initial connection attempt has failed, when starting a transport process after a connection has been accepted, when sending events, when returning options in diameter:service_info/2, and possibly more. In particular, the configuration options below were lost. avp_dictionaries incoming_maxlen spawn_opt strict_mbit The effect was as if these hadn't been configured. Discovered when testing {diameter_dist, route_session, []} as a spawn_opt MFA and seeing that the callback was missing after an initial connection failure. (Thanks to Tamas Bodnar.) Add a simply match to the event suite that detects one instance of the fault. --- lib/diameter/src/base/diameter_service.erl | 4 +-- lib/diameter/test/diameter_event_SUITE.erl | 47 +++++++++++++++++++----------- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 77d184cfc7..a57b7e7f4b 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -880,8 +880,8 @@ start(Ref, Type, Opts, N, #state{watchdogT = WatchdogT, T = {TOpts, SOpts, RecvData, Svc}, Rec = #watchdog{type = Type, ref = Ref, - options = TOpts}, - + options = Opts}, %% original options, returned + %% by service_info/2 diameter_lib:fold_n(fun(_,A) -> [wd(Type, Ref, T, WatchdogT, Rec) | A] end, diff --git a/lib/diameter/test/diameter_event_SUITE.erl b/lib/diameter/test/diameter_event_SUITE.erl index a291dde6be..6d1d1dfd8f 100644 --- a/lib/diameter/test/diameter_event_SUITE.erl +++ b/lib/diameter/test/diameter_event_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2017. All Rights Reserved. +%% Copyright Ericsson AB 2013-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -28,6 +28,8 @@ -export([suite/0, all/0, + init_per_suite/1, + end_per_suite/1, init_per_testcase/2, end_per_testcase/2]). @@ -85,6 +87,13 @@ all() -> cea_timeout, stop]. +%% Not used, but a convenient place to enable trace. +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + init_per_testcase(Name, Config) -> [{name, Name} | Config]. @@ -109,17 +118,19 @@ start_server(Config) -> %% Connect with matching capabilities and expect the connection to %% come up. up(Config) -> - {Svc, Ref} = connect(Config, [{connect_timer, 5000}, - {watchdog_timer, 15000}]), + {Svc, Ref, T} = connect(Config, [{strict_mbit, false}, + {connect_timer, 5000}, + {watchdog_timer, 15000}]), start = event(Svc), - {up, Ref, {TPid, Caps}, Cfg, #diameter_packet{msg = M}} = event(Svc), + {{up, Ref, {TPid, Caps}, T, #diameter_packet{msg = M}}, _} + = {event(Svc), T}, ['CEA' | #{}] = M, %% assert {watchdog, Ref, _, {initial, okay}, _} = event(Svc), %% Kill the transport process and see that the connection is %% reestablished after a watchdog timeout, not after connect_timer %% expiry. exit(TPid, kill), - {down, Ref, {TPid, Caps}, Cfg} = event(Svc), + {{down, Ref, {TPid, Caps}, T}, _} = {event(Svc), T}, {watchdog, Ref, _, {okay, down}, _} = event(Svc), {reconnect, Ref, _} = event(Svc, 10000, 20000). @@ -127,24 +138,25 @@ up(Config) -> %% to indicate as much and then for the transport to be restarted %% (after connect_timer). down(Config) -> - {Svc, Ref} = connect(Config, [{capabilities, [{'Acct-Application-Id', - [?DICT_ACCT:id()]}]}, - {applications, [?DICT_ACCT]}, - {connect_timer, 5000}, - {watchdog_timer, 20000}]), + {Svc, Ref, T} = connect(Config, [{capabilities, [{'Acct-Application-Id', + [?DICT_ACCT:id()]}]}, + {applications, [?DICT_ACCT]}, + {connect_timer, 5000}, + {watchdog_timer, 20000}]), start = event(Svc), - {closed, Ref, {'CEA', ?NO_COMMON_APP, _, #diameter_packet{msg = M}}, _} - = event(Svc), + {{closed, Ref, {'CEA', ?NO_COMMON_APP, _, #diameter_packet{msg = M}}, T}, + _} + = {event(Svc), T}, ['CEA' | #{}] = M, %% assert {reconnect, Ref, _} = event(Svc, 4000, 10000). %% Connect with matching capabilities but have the server delay its %% CEA and cause the client to timeout. cea_timeout(Config) -> - {Svc, Ref} = connect(Config, [{capx_timeout, ?SERVER_CAPX_TMO div 2}, - {connect_timer, 2*?SERVER_CAPX_TMO}]), + {Svc, Ref, T} = connect(Config, [{capx_timeout, ?SERVER_CAPX_TMO div 2}, + {connect_timer, 2*?SERVER_CAPX_TMO}]), start = event(Svc), - {closed, Ref, {'CEA', timeout}, _} = event(Svc). + {{closed, Ref, {'CEA', timeout}, T}, _} = {event(Svc), T}. stop(_Config) -> ok = diameter:stop(). @@ -168,8 +180,9 @@ connect(Config, Opts) -> Name = Pre ++ uniq() ++ ?CLIENT, diameter:subscribe(Name), ok = start_service(Name, ?SERVICE(Name, [?DICT_COMMON, ?DICT_ACCT])), - {ok, Ref} = diameter:add_transport(Name, opts(Config, Opts)), - {Name, Ref}. + {connect, _} = T = opts(Config, Opts), + {ok, Ref} = diameter:add_transport(Name, T), + {Name, Ref, T}. uniq() -> "-" ++ diameter_util:unique_string(). -- cgit v1.2.1 From ffa9d7194e4c4d188fcec21537eefa696b2174b0 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Tue, 11 Feb 2020 22:11:52 +0100 Subject: Fix service/transport option inconsistencies Service options passed to diameter:add_transport/2 were interpreted as such, instead of being ignored as the documentation requires, with the consequence that outgoing and incoming requests saw different values of some options (eg. decode_format), some were always taken from transport options (eg. restrict_connections), and others from service options (eg. share_peers/use_shared_peers). Now ignore service options passed to diameter:add_transport/2, as the documentation requires. This behaviour precedes the changes in commit 5f3becad, that the parent commit addresses, and has probably been the case for some time. That unknown options passed to diameter:add_transport/2 are ignored rather than rejected is questionable, but at least one known user makes use of this to be able to identify transport options returned from diameter:service_info/2, so it can't just be removed. That service options in transport configuration isn't rejected outright is probably even more questionable, but it hasn't been an issue in practice. The documentation was also a little wrong on which transport options can be configured on a service, missing transport_config and transport_module as two that aren't allowed, although these did cause diameter:start_service/2 to return error as expected. (That function not accepting unknown options.) --- lib/diameter/doc/src/diameter.xml | 5 +- lib/diameter/src/base/diameter_config.erl | 20 +++++--- lib/diameter/src/base/diameter_service.erl | 79 +++++++++++++++++++----------- 3 files changed, 65 insertions(+), 39 deletions(-) diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index 0ab6a122cd..aa4eb6ad45 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -1046,8 +1046,9 @@ each node from which requests are sent.

&transport_opt;

-Any transport option except applications or -capabilities. +Any transport option except applications, +capabilities, transport_config, and +transport_module. Used as defaults for transport configuration, values passed to &add_transport; overriding values configured on the service.

diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl index 36ae4c2276..495e57e456 100644 --- a/lib/diameter/src/base/diameter_config.erl +++ b/lib/diameter/src/base/diameter_config.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2018. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -661,6 +661,9 @@ opt(transport, {transport_module, M}) -> opt(transport, {transport_config, _, Tmo}) -> ?IS_UINT32(Tmo) orelse Tmo == infinity; +opt(transport, {transport_config, _}) -> + true; + opt(transport, {applications, As}) -> is_list(As); @@ -720,15 +723,16 @@ opt(_, {K, _}) when K == disconnect_cb; K == capabilities_cb -> true; -opt(transport, {K, _}) - when K == transport_config; - K == private -> +opt(transport, {private, _}) -> true; -%% Anything else, which is ignored in transport config. This makes -%% options sensitive to spelling mistakes, but arbitrary options are -%% passed by some users as a way to identify transports so can't just -%% do away with it. +%% Anything else is ignored in transport config. This makes options +%% sensitive to spelling mistakes and unintentionally passing service +%% options, but arbitrary options are passed by some users as a way to +%% identify transports (they can be returned by diameter:service_info/2 +%% for example) so can't just do away with it, although silently +%% swallowing service options is at least debatable. The documentation +%% says anything, so accept anything. opt(K, _) -> K == transport. diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index a57b7e7f4b..520a7233cc 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2018. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -735,29 +735,31 @@ init_peers() -> %% Alias, %% TPid} +%% Valid service options are all 2-tuples. service_opts(Opts) -> - remove([{strict_arities, true}, - {avp_dictionaries, []}], - maps:merge(maps:from_list([{monitor, false} | def_opts()]), - maps:from_list(Opts))). + remove([{strict_arities, true}, {avp_dictionaries, []}], + merge(lists:append([[{monitor, false}] | def_opts()]), Opts)). + +merge(List1, List2) -> + maps:merge(maps:from_list(List1), maps:from_list(List2)). remove(List, Map) -> maps:filter(fun(K,V) -> not lists:member({K,V}, List) end, Map). -def_opts() -> %% defaults on the service map - [{share_peers, false}, - {use_shared_peers, false}, - {sequence, {0,32}}, - {restrict_connections, nodes}, - {incoming_maxlen, 16#FFFFFF}, - {strict_arities, true}, - {strict_mbit, true}, - {decode_format, record}, - {avp_dictionaries, []}, - {traffic_counters, true}, - {string_decode, true}, - {spawn_opt, []}]. +def_opts() -> %% defaults on the options map + [[{decode_format, record}, %% service options + {restrict_connections, nodes}, + {sequence, {0,32}}, + {share_peers, false}, + {strict_arities, true}, + {string_decode, true}, + {traffic_counters, true}, + {use_shared_peers, false}], + [{avp_dictionaries, []}, %% common options + {incoming_maxlen, 16#FFFFFF}, + {spawn_opt, []}, + {strict_mbit, true}]]. mref(false = No) -> No; @@ -875,9 +877,9 @@ start(Ref, Type, Opts, N, #state{watchdogT = WatchdogT, = Svc1 = merge_service(Opts, Svc0), Svc = binary_caps(Svc1, SD), - {SOpts, TOpts} = merge_opts(SvcOpts, Opts), - RecvData = diameter_traffic:make_recvdata([SvcName, PeerT, Apps, SOpts]), - T = {TOpts, SOpts, RecvData, Svc}, + {Map, Rest} = merge_opts(SvcOpts, Opts), + RecvData = diameter_traffic:make_recvdata([SvcName, PeerT, Apps, Map]), + T = {Rest, Map, RecvData, Svc}, Rec = #watchdog{type = Type, ref = Ref, options = Opts}, %% original options, returned @@ -888,14 +890,33 @@ start(Ref, Type, Opts, N, #state{watchdogT = WatchdogT, [], N). -merge_opts(SvcOpts, Opts) -> - Keys = [K || {K,_} <- def_opts()], - SO = [T || {K,_} = T <- Opts, lists:member(K, Keys)], - TO = Opts -- SO, - {maps:merge(maps:with(Keys, SvcOpts), maps:from_list(SO)), - TO ++ [T || {K,_} = T <- maps:to_list(SvcOpts), - not lists:member(K, Keys), - not lists:keymember(K, 1, Opts)]}. +%% This is awkward. We have service options that have been passed to +%% diameter:start_service/2 (minus application and capabilities +%% options, removed in diameter_config) and transport options passed +%% to diameter:add_transport/2. The former can include defaults for +%% the latter, but the latter can also contain arbitrary options that +%% are just returned by diameter:service_info/2. There's nothing +%% stopping these arbitrary options from being valid service options, +%% but these aren't interpreted as such. +%% +%% The options are merged (transport defaults have already been merged +%% into the service options in service_opts/1) and split into a map +%% for the service options and a few transport options, and a list for +%% the rest. This is historical convolution. Some options are are +%% pulled out of the list on the way to starting the transport process +%% in diameter_peer_fsm, but more work could probably be done here to +%% simplify things. +%% +%% Transport options are not necessarily 2-tuples: the transport_config +%% 3-tuple means they can't just be turned into a map. +merge_opts(SOpts, TOpts) -> + [SD,TD] = Def = def_opts(), + Keys = [K || L <- Def, {K,_} <- L], + Opts = [T || {K,_} = T <- TOpts, lists:keymember(K, 1, TD)], + {maps:merge(maps:with(Keys, SOpts), maps:from_list(Opts)),%% merge TOpts + TOpts ++ [T || {K,_} = T <- maps:to_list(SOpts), %% append SOpts + not lists:keymember(K, 1, SD), + [] == [A || {A,_} <- TOpts, A == K]]}. binary_caps(Svc, true) -> Svc; -- cgit v1.2.1 From 92316dd79418669e06829306b3048a98d7b0c878 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Wed, 12 Feb 2020 13:09:35 +0100 Subject: Skip many more traffic testcases There are currently 55 traffic testcases and 1440 config variants: 55*1440 = 79200 possible testcases. Skipping 25% of these (randomly) still leaves 59400 for common_test to trudge through, and this is not quick, even though some of these 54900 are also skipped: almost 90 minutes on one reasonable host. The benefit of running all these variants of the same test ad nauseum (on many hosts to boot) is doubtful, so increase the number skipped to 90%. This leaves 7920 testcases, still not a small number, and takes around 12 minutes vs 90. (Which is exactly 90 * 10/75 in fact.) --- lib/diameter/test/diameter_traffic_SUITE.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 47b00c25a2..e5c35be9ca 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2019. All Rights Reserved. +%% Copyright Ericsson AB 2010-2020. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -128,7 +128,7 @@ %% =========================================================================== %% Fraction of shuffle/parallel groups to randomly skip. --define(SKIP, 0.25). +-define(SKIP, 0.90). %% Positive number of testcases from which to select (randomly) from %% tc(), the list of testcases to run, or [] to run all. The random -- cgit v1.2.1 From e3db347077f99b455ad671a9d768226ef2905ac0 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Wed, 12 Feb 2020 13:48:11 +0100 Subject: Skip traffic tests differently The reduction of the number of traffic cases in the parent commit speeds up testing, but common_test doesn't seem to do well with the large number of groups returned from groups/0: one for each of the 1440 config variants and a few more. Randomly skip variants when building the groups list instead of when init_per_group/2 is invoked, to reduce the running time from 12 minutes mentioned in the parent commit to around 8. --- lib/diameter/test/diameter_traffic_SUITE.erl | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index e5c35be9ca..452bd28333 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -305,7 +305,8 @@ names() -> S <- ?STRING_DECODES, ST <- ?CALLBACKS, SS <- ?SENDERS, - CS <- ?SENDERS]. + CS <- ?SENDERS, + ?SKIP =< rand:uniform()]. names(Names, []) -> [N || N <- Names, @@ -336,14 +337,9 @@ init_per_group(_) -> init_per_group(Name, Config) when Name == shuffle; Name == parallel -> - case rand:uniform() < ?SKIP of - true -> - {skip, random}; - false -> - start_services(Config), - add_transports(Config), - replace({sleep, Name == parallel}, Config) - end; + start_services(Config), + add_transports(Config), + replace({sleep, Name == parallel}, Config); init_per_group(sctp = Name, Config) -> {_, Sctp} = lists:keyfind(Name, 1, Config), -- cgit v1.2.1