1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2010-2019. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21%% 22%% A diameter callback module that can redirect selected callbacks, 23%% providing reasonable default implementations otherwise. 24%% 25%% To order alternate callbacks, configure a #diameter_callback record 26%% as the Diameter application callback in question. The record has 27%% one field for each callback function as well as 'default' and 28%% 'extra' fields. A function-specific field can be set to a 29%% diameter:eval() in order to redirect the callback 30%% corresponding to that field, or to 'false' to request the default 31%% callback implemented in this module. If neither of these fields are 32%% set then the 'default' field determines the form of the callback: a 33%% module name results in the usual callback as if the module had been 34%% configured directly as the callback module, a diameter_eval() 35%% in a callback applied to the atom-valued callback name and argument 36%% list. For all callbacks not to this module, the 'extra' field is a 37%% list of additional arguments, following arguments supplied by 38%% diameter but preceding those of the diameter:eval() being 39%% applied. 40%% 41%% For example, the following config to diameter:start_service/2, in 42%% an 'application' tuple, would result in only a mymod:peer_down/3 43%% callback, this module implementing the remaining callbacks. 44%% 45%% {module, #diameter_callback{peer_down = {mymod, down, []}}} 46%% 47%% Equivalently, this can also be specified with a [Mod | Args] 48%% field/value list as follows. 49%% 50%% {module, [diameter_callback, {peer_down, {mymod, down, []}}]} 51%% 52%% The following would result in this module suppying peer_up and 53%% peer_down callback, others taking place in module mymod. 54%% 55%% {module, #diameter_callback{peer_up = false, 56%% peer_down = false, 57%% default = mymod}} 58%% 59%% The following would result in all callbacks taking place as 60%% calls to mymod:diameter/2. 61%% 62%% {module, #diameter_callback{default = {mymod, diameter, []}}} 63%% 64%% The following are equivalent and result in all callbacks being 65%% provided by this module. 66%% 67%% {module, #diameter_callback{}} 68%% {module, diameter_callback} 69%% 70 71-module(diameter_callback). 72 73%% Default callbacks when no alternate is specified. 74-export([peer_up/3, 75 peer_down/3, 76 pick_peer/4, 77 prepare_request/3, 78 prepare_retransmit/3, 79 handle_request/3, 80 handle_answer/4, 81 handle_error/4]). 82 83%% Callbacks taking a #diameter_callback record. 84-export([peer_up/4, 85 peer_down/4, 86 pick_peer/5, 87 prepare_request/4, 88 prepare_retransmit/4, 89 handle_request/4, 90 handle_answer/5, 91 handle_error/5]). 92 93-include_lib("diameter/include/diameter.hrl"). 94 95%%% ---------------------------------------------------------- 96%%% # peer_up/3 97%%% ---------------------------------------------------------- 98 99peer_up(_Svc, _Peer, State) -> 100 State. 101 102peer_up(Svc, Peer, State, D) -> 103 cb(peer_up, 104 [Svc, Peer, State], 105 D#diameter_callback.peer_up, 106 D). 107 108%%% ---------------------------------------------------------- 109%%% # peer_down/3 110%%% ---------------------------------------------------------- 111 112peer_down(_Svc, _Peer, State) -> 113 State. 114 115peer_down(Svc, Peer, State, D) -> 116 cb(peer_down, 117 [Svc, Peer, State], 118 D#diameter_callback.peer_down, 119 D). 120 121%%% ---------------------------------------------------------- 122%%% # pick_peer/4 123%%% ---------------------------------------------------------- 124 125pick_peer([Peer|_], _, _Svc, _State) -> 126 {ok, Peer}; 127pick_peer([], _, _Svc, _State) -> 128 false. 129 130pick_peer(PeersL, PeersR, Svc, State, D) -> 131 cb(pick_peer, 132 [PeersL, PeersR, Svc, State], 133 D#diameter_callback.pick_peer, 134 D). 135 136%%% ---------------------------------------------------------- 137%%% # prepare_request/3 138%%% ---------------------------------------------------------- 139 140prepare_request(Pkt, _Svc, _Peer) -> 141 {send, Pkt}. 142 143prepare_request(Pkt, Svc, Peer, D) -> 144 cb(prepare_request, 145 [Pkt, Svc, Peer], 146 D#diameter_callback.prepare_request, 147 D). 148 149%%% ---------------------------------------------------------- 150%%% # prepare_retransmit/3 151%%% ---------------------------------------------------------- 152 153prepare_retransmit(Pkt, _Svc, _Peer) -> 154 {send, Pkt}. 155 156prepare_retransmit(Pkt, Svc, Peer, D) -> 157 cb(prepare_retransmit, 158 [Pkt, Svc, Peer], 159 D#diameter_callback.prepare_retransmit, 160 D). 161 162%%% ---------------------------------------------------------- 163%%% # handle_request/3 164%%% ---------------------------------------------------------- 165 166handle_request(_Pkt, _Svc, _Peer) -> 167 {protocol_error, 3001}. %% DIAMETER_COMMAND_UNSUPPORTED 168 169handle_request(Pkt, Svc, Peer, D) -> 170 cb(handle_request, 171 [Pkt, Svc, Peer], 172 D#diameter_callback.handle_request, 173 D). 174 175%%% ---------------------------------------------------------- 176%%% # handle_answer/4 177%%% ---------------------------------------------------------- 178 179handle_answer(#diameter_packet{msg = Ans, errors = []}, _Req, _Svc, _Peer) -> 180 Ans; 181handle_answer(#diameter_packet{msg = Ans, errors = Es}, _Req, _Svc, _Peer) -> 182 [Ans | Es]. 183 184handle_answer(Pkt, Req, Svc, Peer, D) -> 185 cb(handle_answer, 186 [Pkt, Req, Svc, Peer], 187 D#diameter_callback.handle_answer, 188 D). 189 190%%% --------------------------------------------------------------------------- 191%%% # handle_error/4 192%%% --------------------------------------------------------------------------- 193 194handle_error(Reason, _Req, _Svc, _Peer) -> 195 {error, Reason}. 196 197handle_error(Reason, Req, Svc, Peer, D) -> 198 cb(handle_error, 199 [Reason, Req, Svc, Peer], 200 D#diameter_callback.handle_error, 201 D). 202 203%% =========================================================================== 204 205%% cb/4 206 207%% Unspecified callback: use default field to determine something 208%% appropriate. 209cb(CB, Args, undefined, D) -> 210 cb(CB, Args, D); 211 212%% Explicitly requested default. 213cb(CB, Args, false, _) -> 214 apply(?MODULE, CB, Args); 215 216%% A specified callback. 217cb(_, Args, F, #diameter_callback{extra = X}) -> 218 diameter_lib:eval([[F|X] | Args]). 219 220%% cb/3 221 222%% No user-supplied default: call ours. 223cb(CB, Args, #diameter_callback{default = undefined}) -> 224 apply(?MODULE, CB, Args); 225 226%% Default is a module name: make the usual callback. 227cb(CB, Args, #diameter_callback{default = M, 228 extra = X}) 229 when is_atom(M) -> 230 apply(M, CB, Args ++ X); 231 232%% Default is something else: apply if to callback name and arguments. 233cb(CB, Args, #diameter_callback{default = F, 234 extra = X}) -> 235 diameter_lib:eval([F, CB, Args | X]). 236