aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter/src/base/diameter_callback.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/diameter/src/base/diameter_callback.erl')
-rw-r--r--lib/diameter/src/base/diameter_callback.erl234
1 files changed, 234 insertions, 0 deletions
diff --git a/lib/diameter/src/base/diameter_callback.erl b/lib/diameter/src/base/diameter_callback.erl
new file mode 100644
index 0000000000..90431099b0
--- /dev/null
+++ b/lib/diameter/src/base/diameter_callback.erl
@@ -0,0 +1,234 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% A diameter callback module that can redirect selected callbacks,
+%% providing reasonable default implementations otherwise.
+%%
+%% To order alternate callbacks, configure a #diameter_callback record
+%% as the Diameter application callback in question. The record has
+%% one field for each callback function as well as 'default' and
+%% 'extra' fields. A function-specific field can be set to a
+%% diameter:evaluable() in order to redirect the callback
+%% corresponding to that field, or to 'false' to request the default
+%% callback implemented in this module. If neither of these fields are
+%% set then the 'default' field determines the form of the callback: a
+%% module name results in the usual callback as if the module had been
+%% configured directly as the callback module, a diameter_evaluable()
+%% in a callback applied to the atom-valued callback name and argument
+%% list. For all callbacks not to this module, the 'extra' field is a
+%% list of additional arguments, following arguments supplied by
+%% diameter but preceeding those of the diameter:evaluable() being
+%% applied.
+%%
+%% For example, the following config to diameter:start_service/2, in
+%% an 'application' tuple, would result in only a mymod:peer_down/3
+%% callback, this module implementing the remaining callbacks.
+%%
+%% {module, #diameter_callback{peer_down = {mymod, down, []}}}
+%%
+%% Equivalently, this can also be specified with a [Mod | Args]
+%% field/value list as follows.
+%%
+%% {module, [diameter_callback, {peer_down, {mymod, down, []}}]}
+%%
+%% The following would result in this module suppying peer_up and
+%% peer_down callback, others taking place in module mymod.
+%%
+%% {module, #diameter_callback{peer_up = false,
+%% peer_down = false,
+%% default = mymod}}
+%%
+%% The following would result in all callbacks taking place as
+%% calls to mymod:diameter/2.
+%%
+%% {module, #diameter_callback{default = {mymod, diameter, []}}}
+%%
+%% The following are equivalent and result in all callbacks being
+%% provided by this module.
+%%
+%% {module, #diameter_callback{}}
+%% {module, diameter_callback}
+%%
+
+-module(diameter_callback).
+
+%% Default callbacks when no aleternate is specified.
+-export([peer_up/3,
+ peer_down/3,
+ pick_peer/4,
+ prepare_request/3,
+ prepare_retransmit/3,
+ handle_request/3,
+ handle_answer/4,
+ handle_error/4]).
+
+%% Callbacks taking a #diameter_callback record.
+-export([peer_up/4,
+ peer_down/4,
+ pick_peer/5,
+ prepare_request/4,
+ prepare_retransmit/4,
+ handle_request/4,
+ handle_answer/5,
+ handle_error/5]).
+
+-include_lib("diameter/include/diameter.hrl").
+
+%%% ----------------------------------------------------------
+%%% # peer_up/3
+%%% ----------------------------------------------------------
+
+peer_up(_Svc, _Peer, State) ->
+ State.
+
+peer_up(Svc, Peer, State, D) ->
+ cb(peer_up,
+ [Svc, Peer, State],
+ D#diameter_callback.peer_up,
+ D).
+
+%%% ----------------------------------------------------------
+%%% # peer_down/3
+%%% ----------------------------------------------------------
+
+peer_down(_Svc, _Peer, State) ->
+ State.
+
+peer_down(Svc, Peer, State, D) ->
+ cb(peer_down,
+ [Svc, Peer, State],
+ D#diameter_callback.peer_down,
+ D).
+
+%%% ----------------------------------------------------------
+%%% # pick_peer/4
+%%% ----------------------------------------------------------
+
+pick_peer([Peer|_], _, _Svc, _State) ->
+ {ok, Peer};
+pick_peer([], _, _Svc, _State) ->
+ false.
+
+pick_peer(PeersL, PeersR, Svc, State, D) ->
+ cb(pick_peer,
+ [PeersL, PeersR, Svc, State],
+ D#diameter_callback.pick_peer,
+ D).
+
+%%% ----------------------------------------------------------
+%%% # prepare_request/3
+%%% ----------------------------------------------------------
+
+prepare_request(Pkt, _Svc, _Peer) ->
+ {send, Pkt}.
+
+prepare_request(Pkt, Svc, Peer, D) ->
+ cb(prepare_request,
+ [Pkt, Svc, Peer],
+ D#diameter_callback.prepare_request,
+ D).
+
+%%% ----------------------------------------------------------
+%%% # prepare_retransmit/3
+%%% ----------------------------------------------------------
+
+prepare_retransmit(Pkt, _Svc, _Peer) ->
+ {send, Pkt}.
+
+prepare_retransmit(Pkt, Svc, Peer, D) ->
+ cb(prepare_retransmit,
+ [Pkt, Svc, Peer],
+ D#diameter_callback.prepare_retransmit,
+ D).
+
+%%% ----------------------------------------------------------
+%%% # handle_request/3
+%%% ----------------------------------------------------------
+
+handle_request(_Pkt, _Svc, _Peer) ->
+ {protocol_error, 3001}. %% DIAMETER_COMMAND_UNSUPPORTED
+
+handle_request(Pkt, Svc, Peer, D) ->
+ cb(handle_request,
+ [Pkt, Svc, Peer],
+ D#diameter_callback.handle_request,
+ D).
+
+%%% ----------------------------------------------------------
+%%% # handle_answer/4
+%%% ----------------------------------------------------------
+
+handle_answer(#diameter_packet{msg = Ans, errors = []}, _Req, _Svc, _Peer) ->
+ Ans;
+handle_answer(#diameter_packet{msg = Ans, errors = Es}, _Req, _Svc, _Peer) ->
+ [Ans | Es].
+
+handle_answer(Pkt, Req, Svc, Peer, D) ->
+ cb(handle_answer,
+ [Pkt, Req, Svc, Peer],
+ D#diameter_callback.handle_answer,
+ D).
+
+%%% ---------------------------------------------------------------------------
+%%% # handle_error/4
+%%% ---------------------------------------------------------------------------
+
+handle_error(Reason, _Req, _Svc, _Peer) ->
+ {error, Reason}.
+
+handle_error(Reason, Req, Svc, Peer, D) ->
+ cb(handle_error,
+ [Reason, Req, Svc, Peer],
+ D#diameter_callback.handle_error,
+ D).
+
+%% ===========================================================================
+
+%% cb/4
+
+%% Unspecified callback: use default field to determine something
+%% appropriate.
+cb(CB, Args, undefined, D) ->
+ cb(CB, Args, D);
+
+%% Explicitly requested default.
+cb(CB, Args, false, _) ->
+ apply(?MODULE, CB, Args);
+
+%% A specified callback.
+cb(_, Args, F, #diameter_callback{extra = X}) ->
+ diameter_lib:eval([[F|X] | Args]).
+
+%% cb/3
+
+%% No user-supplied default: call ours.
+cb(CB, Args, #diameter_callback{default = undefined}) ->
+ apply(?MODULE, CB, Args);
+
+%% Default is a module name: make the usual callback.
+cb(CB, Args, #diameter_callback{default = M,
+ extra = X})
+ when is_atom(M) ->
+ apply(M, CB, Args ++ X);
+
+%% Default is something else: apply if to callback name and arguments.
+cb(CB, Args, #diameter_callback{default = F,
+ extra = X}) ->
+ diameter_lib:eval([F, CB, Args | X]).