diff options
Diffstat (limited to 'lib/diameter/src/base/diameter_callback.erl')
-rw-r--r-- | lib/diameter/src/base/diameter_callback.erl | 234 |
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]). |