From 1057d8c7f1bc386e4369c39574cfca97751d9a65 Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Tue, 25 Sep 2012 09:31:54 +0200
Subject: Exit peer_fsm with {shutdown, watchdog_timeout}, not shutdown
This was a remnant of the time when sasl interpreted everything but
shutdown or normal as a crash.
---
lib/diameter/src/base/diameter_watchdog.erl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl
index d7474e5c56..53f5f42396 100644
--- a/lib/diameter/src/base/diameter_watchdog.erl
+++ b/lib/diameter/src/base/diameter_watchdog.erl
@@ -555,7 +555,7 @@ timeout(#watchdog{status = T,
= S)
when T == suspect;
T == reopen, P, N < 0 ->
- exit(TPid, shutdown),
+ exit(TPid, {shutdown, watchdog_timeout}),
close(S),
S#watchdog{status = down};
--
cgit v1.2.3
From 2d3a6154748061edd66b721bc11dbed9046d49ff Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Tue, 25 Sep 2012 17:40:17 +0200
Subject: Fix handling of Origin-State-Id config
Documentation (correctly) say {'Origin-State-Id', Unsigned32()},
code (incorrectly) expected {'Origin-State-Id', [Unsigned32()]}.
---
lib/diameter/src/base/diameter_capx.erl | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lib/diameter/src/base/diameter_capx.erl b/lib/diameter/src/base/diameter_capx.erl
index 6c4d60ee9b..6a87819f04 100644
--- a/lib/diameter/src/base/diameter_capx.erl
+++ b/lib/diameter/src/base/diameter_capx.erl
@@ -141,7 +141,9 @@ cap('Host-IP-Address', Vs)
when is_list(Vs) ->
lists:map(fun ipaddr/1, Vs);
-cap('Firmware-Revision', V) ->
+cap(K, V)
+ when K == 'Firmware-Revision';
+ K == 'Origin-State-Id' ->
[V];
cap(_, Vs)
--
cgit v1.2.3
From e02ab3b7c6129d59009dda5a9a357edecd3258d7 Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Tue, 25 Sep 2012 18:11:51 +0200
Subject: Fix matching in case of erroneous capabilities config
{invalid, K, V} was never matched. Return full reason, not just an
atom.
---
lib/diameter/src/base/diameter_capx.erl | 2 +-
lib/diameter/src/base/diameter_config.erl | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/diameter/src/base/diameter_capx.erl b/lib/diameter/src/base/diameter_capx.erl
index 6a87819f04..190d37262b 100644
--- a/lib/diameter/src/base/diameter_capx.erl
+++ b/lib/diameter/src/base/diameter_capx.erl
@@ -151,7 +151,7 @@ cap(_, Vs)
Vs;
cap(K, V) ->
- ?THROW({invalid, K, V}).
+ ?THROW({invalid, {K,V}}).
ipaddr(A) ->
try
diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl
index e47f63f814..d1916c26e6 100644
--- a/lib/diameter/src/base/diameter_config.erl
+++ b/lib/diameter/src/base/diameter_config.erl
@@ -563,7 +563,7 @@ make_caps(Caps, Opts) ->
case diameter_capx:make_caps(Caps, Opts) of
{ok, T} ->
T;
- {error, {Reason, _}} ->
+ {error, Reason} ->
?THROW(Reason)
end.
--
cgit v1.2.3
From 08b325d1a4890ffe4f9ca5061f017384947a56f6 Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Mon, 1 Oct 2012 08:45:23 +0200
Subject: Send up event *after* inserting peer in table
Otherwise a request in response to an event can return error due to the
peer not yet having been inserted.
---
lib/diameter/src/base/diameter_service.erl | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index 725cccda1e..131c5d9837 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -979,10 +979,9 @@ connection_up(T, P, C, #state{peerT = PeerT,
insert(PeerT, P#peer{op_state = {?STATE_UP, ?WD_OKAY}}),
request_peer_up(TPid),
+ insert_local_peer(SApps, {{TPid, Caps}, {SvcName, Apps}}, LDict),
report_status(up, P, C, S, T),
- S#state{local_peers = insert_local_peer(SApps,
- {{TPid, Caps}, {SvcName, Apps}},
- LDict)}.
+ S.
insert_local_peer(SApps, T, LDict) ->
lists:foldl(fun(A,D) -> ilp(A, T, D) end, LDict, SApps).
@@ -1058,12 +1057,9 @@ connection_down(#peer{conn = TPid,
local_peers = LDict}
= S) ->
report_status(down, P, C, S, []),
- NewS = S#state{local_peers
- = remove_local_peer(SApps,
- {{TPid, Caps}, {SvcName, Apps}},
- LDict)},
- request_peer_down(TPid, NewS),
- NewS.
+ remove_local_peer(SApps, {{TPid, Caps}, {SvcName, Apps}}, LDict),
+ request_peer_down(TPid, S),
+ S.
remove_local_peer(SApps, T, LDict) ->
lists:foldl(fun(A,D) -> rlp(A, T, D) end, LDict, SApps).
--
cgit v1.2.3
From ead9e5325c52a7271796c9e3a053d50cd86a189c Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Wed, 3 Oct 2012 11:41:29 +0200
Subject: Add missing clause for peer failover
diameter_codec:sequence_numbers/1 is called on an already extracted pair
of sequence numbers in the case of failover.
---
lib/diameter/src/base/diameter_codec.erl | 3 +++
1 file changed, 3 insertions(+)
diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl
index 421e280422..a94d37f7a8 100644
--- a/lib/diameter/src/base/diameter_codec.erl
+++ b/lib/diameter/src/base/diameter_codec.erl
@@ -333,6 +333,9 @@ decode_header(_) ->
%% wraparound counter. The 8-bit counter is incremented each time the
%% system is restarted.
+sequence_numbers({_,_} = T) ->
+ T;
+
sequence_numbers(#diameter_packet{bin = Bin})
when is_binary(Bin) ->
sequence_numbers(Bin);
--
cgit v1.2.3
From a5eb60194ae2e028461604561f2ebd7c52f672ca Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Tue, 2 Oct 2012 10:23:32 +0200
Subject: Don't call service process for service_info
So that the function is usable when we're already in the service
process. For example, in peer_up/peer_down callbacks.
---
lib/diameter/src/base/diameter_service.erl | 39 ++++++++++++++++++++++--------
1 file changed, 29 insertions(+), 10 deletions(-)
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index 725cccda1e..7da64c1846 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -236,12 +236,12 @@ stop_transport(SvcName, [_|_] = Refs) ->
%%% ---------------------------------------------------------------------------
info(SvcName, Item) ->
- info_rc(call_service_by_name(SvcName, {info, Item})).
-
-info_rc({error, _}) ->
- undefined;
-info_rc(Info) ->
- Info.
+ case ets:lookup(?STATE_TABLE, SvcName) of
+ [] ->
+ undefined;
+ [S] ->
+ service_info(Item, S)
+ end.
%%% ---------------------------------------------------------------------------
%%% # receive_message(TPid, Pkt, MessageData)
@@ -462,6 +462,7 @@ handle_call({pick_peer, Local, Remote, App}, _From, S) ->
handle_call({call_module, AppMod, Req}, From, S) ->
call_module(AppMod, Req, From, S);
+%% Call from old code.
handle_call({info, Item}, _From, S) ->
{reply, service_info(Item, S), S};
@@ -2955,7 +2956,12 @@ info_stats(#state{peerT = PeerT}) ->
MatchSpec = [{#peer{ref = '$1', conn = '$2', _ = '_'},
[{'is_pid', '$2'}],
[['$1', '$2']]}],
- diameter_stats:read(lists:append(ets:select(PeerT, MatchSpec))).
+ try ets:select(PeerT, MatchSpec) of
+ L ->
+ diameter_stats:read(lists:append(L))
+ catch
+ error: badarg -> [] %% service has gone down
+ end.
%% info_transport/1
%%
@@ -3000,7 +3006,12 @@ transport([[{type, accept}, {options, Opts} | _] | _] = Ls) ->
{accept, [lists:nthtail(2,L) || L <- Ls]}].
peer_dict(#state{peerT = PeerT, connT = ConnT}, Dict0) ->
- ets:foldl(fun(T,A) -> peer_acc(ConnT, A, T) end, Dict0, PeerT).
+ try ets:tab2list(PeerT) of
+ L ->
+ lists:foldl(fun(T,A) -> peer_acc(ConnT, A, T) end, Dict0, L)
+ catch
+ error: badarg -> Dict0 %% service has gone down
+ end.
peer_acc(ConnT, Acc, #peer{pid = Pid,
type = Type,
@@ -3019,7 +3030,11 @@ peer_acc(ConnT, Acc, #peer{pid = Pid,
info_conn(ConnT, TPid, true)
when is_pid(TPid) ->
- info_conn(ets:lookup(ConnT, TPid));
+ try ets:lookup(ConnT, TPid) of
+ T -> info_conn(T)
+ catch
+ error: badarg -> [] %% service has gone down
+ end;
info_conn(_, _, _) ->
[].
@@ -3096,7 +3111,11 @@ info_pending(#state{} = S) ->
{{transport, '$2'}},
{{from, '$3'}}]}}]}],
- ets:select(?REQUEST_TABLE, MatchSpec).
+ try
+ ets:select(?REQUEST_TABLE, MatchSpec)
+ catch
+ error: badarg -> [] %% service has gone down
+ end.
%% info_connections/1
%%
--
cgit v1.2.3
From f24adb28f51ea35d13dcc501920ef727dfb2d61b Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Fri, 21 Sep 2012 10:29:00 +0200
Subject: Add eval_packet for examining outgoing messages after encode
Both prepare_request/prepare_retransmit and handle_request can
return {eval_packet, RC, PostF} where PostF will be evaluated on any
encoded packet before transmission.
---
lib/diameter/src/base/diameter_service.erl | 194 ++++++++++++++++++-----------
1 file changed, 122 insertions(+), 72 deletions(-)
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index 725cccda1e..01c50a8e30 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -1301,28 +1301,34 @@ cm([_,_|_], _, _, _) ->
%% The mod field of the #diameter_app{} here includes any extra
%% arguments passed to diameter:call/2.
-send_request({TPid, Caps, App}, Msg, Opts, Caller, SvcName) ->
+send_request({TPid, Caps, App} = T, Msg, Opts, Caller, SvcName) ->
#diameter_app{module = ModX}
= App,
Pkt = make_request_packet(Msg),
- case cb(ModX, prepare_request, [Pkt, SvcName, {TPid, Caps}]) of
- {send, P} ->
- send_request(make_request_packet(P, Pkt),
- TPid,
- Caps,
- App,
- Opts,
- Caller,
- SvcName);
- {discard, Reason} ->
- {error, Reason};
- discard ->
- {error, discarded};
- T ->
- ?ERROR({invalid_return, prepare_request, App, T})
- end.
+ send_req(cb(ModX, prepare_request, [Pkt, SvcName, {TPid, Caps}]),
+ Pkt,
+ T,
+ Opts,
+ Caller,
+ SvcName,
+ []).
+
+send_req({send, P}, Pkt, T, Opts, Caller, SvcName, Fs) ->
+ send_request(make_request_packet(P, Pkt), T, Opts, Caller, SvcName, Fs);
+
+send_req({discard, Reason} , _, _, _, _, _, _) ->
+ {error, Reason};
+
+send_req(discard, _, _, _, _, _, _) ->
+ {error, discarded};
+
+send_req({eval_packet, RC, F}, Pkt, T, Opts, Caller, SvcName, Fs) ->
+ send_req(RC, Pkt, T, Opts, Caller, SvcName, [F|Fs]);
+
+send_req(E, _, {_, _, App}, _, _, _, _) ->
+ ?ERROR({invalid_return, prepare_request, App, E}).
%% make_request_packet/1
%%
@@ -1400,16 +1406,16 @@ fold_record(undefined, R) ->
fold_record(Rec, R) ->
diameter_lib:fold_tuple(2, Rec, R).
-%% send_request/7
+%% send_request/6
-send_request(Pkt, TPid, Caps, App, Opts, Caller, SvcName) ->
+send_request(Pkt, {TPid, Caps, App}, Opts, Caller, SvcName, Fs) ->
#diameter_app{alias = Alias,
dictionary = Dict,
module = ModX,
options = [{answer_errors, AE} | _]}
= App,
- EPkt = encode(Dict, Pkt),
+ EPkt = encode(Dict, Pkt, Fs),
#options{filter = Filter,
timeout = Timeout}
@@ -1490,6 +1496,13 @@ msg(#diameter_packet{msg = undefined, bin = Bin}) ->
msg(#diameter_packet{msg = Msg}) ->
Msg.
+%% encode/3
+
+encode(Dict, Pkt, Fs) ->
+ P = encode(Dict, Pkt),
+ eval_packet(P, Fs),
+ P.
+
%% encode/2
%% Note that prepare_request can return a diameter_packet containing
@@ -1571,38 +1584,47 @@ send(Pid, Pkt) ->
%% retransmit/4
-retransmit({TPid, Caps, #diameter_app{alias = Alias} = App},
- #request{app = Alias,
- packet = Pkt}
+retransmit({TPid, Caps, #diameter_app{alias = Alias} = App} = T,
+ #request{app = Alias, packet = Pkt}
= Req,
SvcName,
Timeout) ->
have_request(Pkt, TPid) %% Don't failover to a peer we've
andalso ?THROW(timeout), %% already sent to.
- case cb(App, prepare_retransmit, [Pkt, SvcName, {TPid, Caps}]) of
- {send, P} ->
- retransmit(make_request_packet(P, Pkt), TPid, Caps, Req, Timeout);
- {discard, Reason} ->
- ?THROW(Reason);
- discard ->
- ?THROW(discarded);
- T ->
- ?ERROR({invalid_return, prepare_retransmit, App, T})
- end.
+ resend_req(cb(App, prepare_retransmit, [Pkt, SvcName, {TPid, Caps}]),
+ T,
+ Req,
+ Timeout,
+ []).
-%% retransmit/5
+resend_req({send, P}, T, #request{packet = Pkt} = Req, Timeout, Fs) ->
+ retransmit(make_request_packet(P, Pkt), T, Req, Timeout, Fs);
-retransmit(Pkt, TPid, Caps, #request{dictionary = Dict} = Req, Timeout) ->
- EPkt = encode(Dict, Pkt),
+resend_req({discard, Reason}, _, _, _, _) ->
+ ?THROW(Reason);
- NewReq = Req#request{transport = TPid,
- packet = Pkt,
- caps = Caps},
+resend_req(discard, _, _, _, _) ->
+ ?THROW(discarded);
- ?LOG(retransmission, NewReq),
- TRef = send_request(TPid, EPkt, NewReq, Timeout),
- {TRef, NewReq}.
+resend_req({eval_packet, RC, F}, T, Req, Timeout, Fs) ->
+ resend_req(RC, T, Req, Timeout, [F|Fs]);
+
+resend_req(T, {_, _, App}, _, _, _) ->
+ ?ERROR({invalid_return, prepare_retransmit, App, T}).
+
+%% retransmit/6
+
+retransmit(Pkt, {TPid, Caps, _}, #request{dictionary = D} = Req0, Tmo, Fs) ->
+ EPkt = encode(D, Pkt, Fs),
+
+ Req = Req0#request{transport = TPid,
+ packet = Pkt,
+ caps = Caps},
+
+ ?LOG(retransmission, Req),
+ TRef = send_request(TPid, EPkt, Req, Tmo),
+ {TRef, Req}.
%% store_request/4
@@ -1805,7 +1827,12 @@ recv_request(T, TC, App, Pkt) ->
%% (3xxx) errors that lead to an answer-message.
request_cb({SvcName, _OH, _OR} = T, TC, App, Pkt) ->
- request_cb(cb(App, handle_request, [Pkt, SvcName, TC]), App, T, TC, Pkt).
+ request_cb(cb(App, handle_request, [Pkt, SvcName, TC]),
+ App,
+ T,
+ TC,
+ [],
+ Pkt).
%% examine/1
%%
@@ -1825,7 +1852,7 @@ examine(#diameter_packet{errors = Es} = Pkt) ->
Pkt#diameter_packet{errors = [5011 | Es]}.
%% It's odd/unfortunate that this isn't a protocol error.
-%% request_cb/5
+%% request_cb/6
%% A reply may be an answer-message, constructed either here or by
%% the handle_request callback. The header from the incoming request
@@ -1836,20 +1863,21 @@ request_cb({reply, Ans},
#diameter_app{dictionary = Dict},
_,
{TPid, _},
+ Fs,
Pkt) ->
- reply(Ans, Dict, TPid, Pkt);
+ reply(Ans, Dict, TPid, Fs, Pkt);
%% An 3xxx result code, for which the E-bit is set in the header.
-request_cb({protocol_error, RC}, _, T, {TPid, _}, Pkt)
+request_cb({protocol_error, RC}, _, T, {TPid, _}, Fs, Pkt)
when 3000 =< RC, RC < 4000 ->
- protocol_error(RC, T, TPid, Pkt);
+ protocol_error(RC, T, TPid, Fs, Pkt);
%% RFC 3588 says we must reply 3001 to anything unrecognized or
%% unsupported. 'noreply' is undocumented (and inappropriately named)
%% backwards compatibility for this, protocol_error the documented
%% alternative.
-request_cb(noreply, _, T, {TPid, _}, Pkt) ->
- protocol_error(3001, T, TPid, Pkt);
+request_cb(noreply, _, T, {TPid, _}, Fs, Pkt) ->
+ protocol_error(3001, T, TPid, Fs, Pkt);
%% 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
@@ -1871,26 +1899,36 @@ request_cb({A, Opts},
= App,
T,
TC,
+ Fs,
Pkt)
when A == relay, Id == ?APP_ID_RELAY;
A == proxy, Id /= ?APP_ID_RELAY;
A == resend ->
- resend(Opts, App, T, TC, Pkt);
+ resend(Opts, App, T, TC, Fs, Pkt);
-request_cb(discard, _, _, _, _) ->
+request_cb(discard, _, _, _, _, _) ->
ok;
-request_cb({eval, RC, F}, App, T, TC, Pkt) ->
- request_cb(RC, App, T, TC, Pkt),
+request_cb({eval_packet, RC, F}, App, T, TC, Fs, Pkt) ->
+ request_cb(RC, App, T, TC, [F|Fs], Pkt);
+
+request_cb({eval, RC, F}, App, T, TC, Fs, Pkt) ->
+ request_cb(RC, App, T, TC, Pkt, Fs),
diameter_lib:eval(F).
-%% protocol_error/4
+%% protocol_error/5
-protocol_error(RC, {_, OH, OR}, TPid, #diameter_packet{avps = Avps} = Pkt) ->
+protocol_error(RC, {_, OH, OR}, TPid, Fs, Pkt) ->
+ #diameter_packet{avps = Avps} = Pkt,
?LOG({error, RC}, Pkt),
- reply(answer_message({OH, OR, RC}, Avps), ?BASE, TPid, Pkt).
+ reply(answer_message({OH, OR, RC}, Avps), ?BASE, TPid, Fs, Pkt).
+
+%% protocol_error/4
-%% resend/5
+protocol_error(RC, T, TPid, Pkt) ->
+ protocol_error(RC, T, TPid, [], Pkt).
+
+%% resend/6
%%
%% Resend a message as a relay or proxy agent.
@@ -1898,9 +1936,12 @@ resend(Opts,
#diameter_app{} = App,
{_SvcName, OH, _OR} = T,
{_TPid, _Caps} = TC,
+ Fs,
#diameter_packet{avps = Avps} = Pkt) ->
{Code, _Flags, Vid} = ?BASE:avp_header('Route-Record'),
- resend(is_loop(Code, Vid, OH, Avps), Opts, App, T, TC, Pkt).
+ resend(is_loop(Code, Vid, OH, Avps), Opts, App, T, TC, Fs, Pkt).
+
+%% resend/7
%% DIAMETER_LOOP_DETECTED 3005
%% An agent detected a loop while trying to get the message to the
@@ -1908,8 +1949,8 @@ resend(Opts,
%% if one is available, but the peer reporting the error has
%% identified a configuration problem.
-resend(true, _, _, T, {TPid, _}, Pkt) -> %% Route-Record loop
- protocol_error(3005, T, TPid, Pkt);
+resend(true, _, _, T, {TPid, _}, Fs, Pkt) -> %% Route-Record loop
+ protocol_error(3005, T, TPid, Fs, Pkt);
%% 6.1.8. Relaying and Proxying Requests
%%
@@ -1922,6 +1963,7 @@ resend(false,
App,
{SvcName, _, _} = T,
{TPid, #diameter_caps{origin_host = {_, OH}}},
+ Fs,
#diameter_packet{header = Hdr0,
avps = Avps}
= Pkt) ->
@@ -1929,7 +1971,7 @@ resend(false,
Seq = diameter_session:sequence(),
Hdr = Hdr0#diameter_header{hop_by_hop_id = Seq},
Msg = [Hdr, Route | Avps],
- resend(call(SvcName, App, Msg, Opts), T, TPid, Pkt).
+ resend(call(SvcName, App, Msg, Opts), T, TPid, Fs, Pkt).
%% The incoming request is relayed with the addition of a
%% Route-Record. Note the requirement on the return from call/4 below,
%% which places a requirement on the value returned by the
@@ -1955,15 +1997,18 @@ resend(#diameter_packet{bin = B}
= Pkt,
_,
TPid,
+ Fs,
#diameter_packet{header = #diameter_header{hop_by_hop_id = Id},
transport_data = TD}) ->
- send(TPid, Pkt#diameter_packet{bin = diameter_codec:hop_by_hop_id(Id, B),
- transport_data = TD});
+ P = Pkt#diameter_packet{bin = diameter_codec:hop_by_hop_id(Id, B),
+ transport_data = TD},
+ eval_packet(P, Fs),
+ send(TPid, P);
%% TODO: counters
%% Or not: DIAMETER_UNABLE_TO_DELIVER.
-resend(_, T, TPid, Pkt) ->
- protocol_error(3002, T, TPid, Pkt).
+resend(_, T, TPid, Fs, Pkt) ->
+ protocol_error(3002, T, TPid, Fs, Pkt).
%% is_loop/4
%%
@@ -1985,33 +2030,38 @@ is_loop(Code, Vid, OH, [_ | Avps])
is_loop(Code, Vid, OH, Avps) ->
is_loop(Code, Vid, ?BASE:avp(encode, OH, 'Route-Record'), Avps).
-%% reply/4
+%% reply/5
%%
%% Send a locally originating reply.
%% Skip the setting of Result-Code and Failed-AVP's below.
-reply([Msg], Dict, TPid, Pkt)
+reply([Msg], Dict, TPid, Fs, Pkt)
when is_list(Msg);
is_tuple(Msg) ->
- reply(Msg, Dict, TPid, Pkt#diameter_packet{errors = []});
+ reply(Msg, Dict, TPid, Fs, Pkt#diameter_packet{errors = []});
%% No errors or a diameter_header/avp list.
-reply(Msg, Dict, TPid, #diameter_packet{errors = Es,
- transport_data = TD}
- = ReqPkt)
+reply(Msg, Dict, TPid, Fs, #diameter_packet{errors = Es,
+ transport_data = TD}
+ = ReqPkt)
when [] == Es;
is_record(hd(Msg), diameter_header) ->
Pkt = diameter_codec:encode(Dict, make_answer_packet(Msg, ReqPkt)),
+ eval_packet(Pkt, Fs),
incr(send, Pkt, Dict, TPid), %% count result codes in sent answers
send(TPid, Pkt#diameter_packet{transport_data = TD});
%% Or not: set Result-Code and Failed-AVP AVP's.
-reply(Msg, Dict, TPid, #diameter_packet{errors = [H|_] = Es} = Pkt) ->
+reply(Msg, Dict, TPid, Fs, #diameter_packet{errors = [H|_] = Es} = Pkt) ->
reply(rc(Msg, rc(H), [A || {_,A} <- Es], Dict),
Dict,
TPid,
+ Fs,
Pkt#diameter_packet{errors = []}).
+eval_packet(Pkt, Fs) ->
+ lists:foreach(fun(F) -> diameter_lib:eval([F,Pkt]) end, Fs).
+
%% make_answer_packet/2
%% Binaries and header/avp lists are sent as-is.
--
cgit v1.2.3
From 98836f68bd01ef82df5b9816a3ff065f02e50156 Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Thu, 4 Oct 2012 19:12:05 +0200
Subject: Document eval_packet
---
lib/diameter/doc/src/diameter_app.xml | 67 +++++++++++++++++++++++++----------
1 file changed, 49 insertions(+), 18 deletions(-)
diff --git a/lib/diameter/doc/src/diameter_app.xml b/lib/diameter/doc/src/diameter_app.xml
index 4a4b212787..98c8b8c807 100644
--- a/lib/diameter/doc/src/diameter_app.xml
+++ b/lib/diameter/doc/src/diameter_app.xml
@@ -325,32 +325,44 @@ or peer_down/3 callback.
Packet = packet()
SvcName = diameter:service_name()
Peer = peer()
-Action = {send, packet() | message()} | {discard, Reason} | discard
+Action = Send | Discard | {eval_packet, Action, PostF}
+Send = {send, packet()
+ | message()}
+Discard = {discard, Reason} | discard
+PostF =
+ diameter:evaluable()}
Invoked to return a request for encoding and transport.
-Allows the sender to access the selected peer's capabilities
-in order to set (for example) Destination-Host and/or
-Destination-Realm in the outgoing request, although the
-callback need not be limited to this usage.
+Allows the sender to use the selected peer's capabilities
+to modify the outgoing request.
Many implementations may simply want to return {send, Packet}
-A returned packet() should set the request to be encoded in its
+A returned packet() should set the
+request to be encoded in its
msg field and can set the transport_data field in order
-to pass information to the transport module.
+to pass information to the transport process.
Extra arguments passed to diameter:call/4 can be used to
-communicate transport data to the callback.
-A returned packet() can also set the header field to a
-#diameter_header{} record in order to specify values that should
-be preserved in the outgoing request, although this should typically
-not be necessary and allows the callback to set header values
-inappropriately.
+communicate transport (or any other) data to the callback.
+
+
+A returned packet() can set
+the header field to a
+#diameter_header{} in order to specify values that should
+be preserved in the outgoing request, values otherwise being those in
+the header record contained in Packet.
A returned length, cmd_code or application_id is
ignored.
+
+A returned PostF will be evaluated on any encoded
+#diameter_packet{} prior to transmission, the bin field
+of this record containing the encoded binary.
+The return value is ignored.
+
Returning {discard, Reason} causes the request to be aborted
and the .
-Mod:prepare_retransmit(Packet, SvcName, Peer) -> Result
+Mod:prepare_retransmit(Packet, SvcName, Peer) -> Action
Return a request for encoding and retransmission.
Packet = packet()
SvcName = diameter:service_name()
-Peer = peer()
-Result = {send, packet() | message()} | {discard, Reason} | discard
+Peer = peer()
+Action = Send | Discard | {eval_packet, Action, PostF}
+Send = {send, packet()
+ | message()}
+Discard = {discard, Reason} | discard
+PostF =
+ diameter:evaluable()}
@@ -484,7 +501,11 @@ callback returned false.
Packet = packet()
SvcName = term()
Peer = peer()
-Action = Reply | {relay, [Opt]} | discard | {eval, Action, PostF}
+Action = Reply
+ | {relay, [Opt]}
+ | discard
+ | {eval, Action, PostF}
+ | {eval_packet, Action, PostF}
Reply = {reply, message()}
| {protocol_error, 3000..3999}
Opt = diameter:call_opt()
@@ -610,7 +631,17 @@ Discard the request.
-
Handle the request as if Action has been returned and then
-evaluate PostF in the request process.
+evaluate PostF in the request process.
+The return value is ignored.
+
+
+{eval_packet, Action, PostF}
+-
+
+Like eval but evaluate PostF on any encoded
+#diameter_packet{} prior to transmission, the bin field
+of this record containing the encoded binary.
+The return value is ignored.
--
cgit v1.2.3
From e28ced7b0be4282063ca111782f13f4ba4f6c3ac Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Fri, 5 Oct 2012 01:47:30 +0200
Subject: Improve other diameter_app doc
---
lib/diameter/doc/src/diameter_app.xml | 192 ++++++++++++++++++++--------------
1 file changed, 112 insertions(+), 80 deletions(-)
diff --git a/lib/diameter/doc/src/diameter_app.xml b/lib/diameter/doc/src/diameter_app.xml
index 98c8b8c807..9d8a6568eb 100644
--- a/lib/diameter/doc/src/diameter_app.xml
+++ b/lib/diameter/doc/src/diameter_app.xml
@@ -112,7 +112,8 @@ and, for the call-specific callbacks, any extra arguments passed to
-
A record containing the identities of
-the local Diameter node and the remote Diameter peer having an established transport
+the local Diameter node and the remote Diameter peer having an
+established transport
connection, as well as the capabilities as
determined by capabilities exchange.
Each field of the record is a 2-tuple consisting of
@@ -168,13 +169,14 @@ Fields should not be set in return values except as documented.
peer_ref() = term()
-
-A term identifying a transport connection with a Diameter peer.
-Should be treated opaquely.
+A term identifying a transport connection with a Diameter peer.
-peer() = {peer_ref(), capabilities()}
+peer() =
+ {peer_ref(),
+ capabilities()}
-
A tuple representing a Diameter peer connection.
@@ -219,10 +221,29 @@ process.
-Invoked when a transport connection has been established
-and a successful capabilities exchange has indicated that the peer
-supports the Diameter application of the application on which
-the callback module in question has been configured.
+Invoked to signal the availability of a peer connection.
+In particular, capabilities exchange with the peer has indicated
+support for the application in question, the RFC 3539 watchdog state
+machine for the connection has reached state OKAY and Diameter
+messages can be both sent and received.
+
+
+
+A watchdog state machine can reach state OKAY from state
+SUSPECT without a new capabilities exchange taking place.
+A new transport connection (and capabilities exchange) results in a
+new peer_ref().
+
+
+
+
+There is no requirement that a callback return before incoming
+requests are received: handle_request/3 callbacks must be
+handled independently of peer_up/3 and peer_down/3.
+
@@ -238,36 +259,42 @@ the callback module in question has been configured.
-Invoked when a transport connection has been lost following a previous
-call to peer_up/3.
+Invoked to signal that a peer connection is no longer available
+following a previous call to peer_up/3.
+In particular, that the RFC 3539 watchdog state machine for the
+connection has left state OKAY and the peer will no longer be a
+candidate in pick_peer()
+callbacks.
-Mod:pick_peer(Candidates, Reserved, SvcName, State)
- -> {ok, Peer} | {Peer, NewState} | false
+Mod:pick_peer(Candidates, _Reserved, SvcName, State)
+ -> Selection | false
Select a target peer for an outgoing request.
Candidates = [peer()]
-Peer = peer() | false
SvcName = diameter:service_name()
State = NewState = state()
+Selection = {ok, Peer} | {Peer, NewState}
+Peer = peer() | false
Invoked as a consequence of a call to diameter:call/4 to select a destination
-peer for an outgoing request, the return value indicating the selected
-peer.
+peer for an outgoing request.
+The return value indicates the selected peer.
-The candidate peers list will only include those
-which are selected by any filter option specified in the call to
-diameter:call/4, and only
-those which have indicated support for the Diameter application in
-question.
+The candidate list contains only those peers that have advertised
+support for the Diameter application in question during capabilities
+exchange, that have not be excluded by a filter option in
+the call to diameter:call/4
+and whose watchdog state machine is in the OKAY state.
The order of the elements is unspecified except that any
peers whose Origin-Host and Origin-Realm matches that of the
outgoing request (in the sense of a {filter, {all, [host, realm]}}
@@ -275,36 +302,40 @@ option to diameter:call/4)
will be placed at the head of the list.
-The return values false and {false, State} are
-equivalent when callback state is mutable, as are
-{ok, Peer} and {Peer, State}.
-Returning a peer as false causes {error, no_connection}
-to be returned from diameter:call/4.
-Returning a peer() from an initial
-pick_peer/4 callback will result in a
-prepare_request/3 callback
-followed by either handle_answer/4
+A callback that returns a peer() will be followed by a
+prepare_request/3
+callback and, if the latter indicates that the request should be sent,
+by either handle_answer/4
or handle_error/4 depending
on whether or not an answer message is received from the peer.
-If transport with the peer is lost before this then a new pick_peer/4 callback takes place to
-select an alternate peer.
-
-
-Note that there is no guarantee that a prepare_request/3 then a new pick_peer/4 callback may take place to
+failover to an alternate peer, after which prepare_retransmit/3 takes the
+place of prepare_request/3 in resending the
+request.
+There is no guarantee that a pick_peer/4 callback to select
-an alternate peer will be followed by any additional callbacks, only
-that the initial pick_peer/4 will be, since a
+an alternate peer will be followed by any additional callbacks since a
retransmission to an alternate peer is abandoned if an answer is
received from a previously selected peer.
+
+Returning false or {false, NewState} causes {error,
+no_connection} to be returned from diameter:call/4.
+
+
+The return values false and {false, State} (that is,
+NewState = State) are equivalent, as are {ok, Peer} and
+{Peer, State}.
+
-{Peer, NewState} and its equivalents can only be returned if
-the Diameter application in question was
-configured with the {Peer, NewState} is only allowed if
+the Diameter application in question was configured with the diameter:application_opt()
{call_mutates_state, true}.
Otherwise, the State argument is always
@@ -360,7 +391,7 @@ ignored.
A returned PostF will be evaluated on any encoded
#diameter_packet{} prior to transmission, the bin field
-of this record containing the encoded binary.
+containing the encoded binary.
The return value is ignored.
@@ -395,8 +426,9 @@ Invoked to return a request for encoding and retransmission.
Has the same role as prepare_request/3 in the case that
a peer connection is lost an an alternate peer selected but the
-argument packet() is as returned by the initial
-prepare_request/3.
+argument packet() is as returned
+by the initial prepare_request/3.
Returning {discard, Reason} causes the request to be aborted
@@ -423,40 +455,41 @@ discarded}.
Invoked when an answer message is received from a peer.
-The return value is returned from the call to diameter:call/4 for which the
-callback takes place unless the detach option was
-specified.
+The return value is returned from diameter:call/4 unless the
+detach option was specified.
-The decoded answer record is in the msg field of the argument
-packet(),
-the undecoded binary in the packet field.
+The decoded answer record and undecoded binary are in the msg
+and bin fields of the argument
+packet() respectively.
Request is the outgoing request message as was returned from
prepare_request/3 or
-prepare_retransmit/3
-before the request was passed to the transport.
+prepare_retransmit/3.
For any given call to diameter:call/4 there is at most one
-call to the handle_answer callback of the application in question: any
+handle_answer/4 callback: any
duplicate answer (due to retransmission or otherwise) is discarded.
-Similarly, only one of handle_answer/4 or handle_error/4 is
-called for any given request.
+Similarly, only one of handle_answer/4 or
+handle_error/4 is
+called.
By default, an incoming answer message that cannot be successfully
-decoded causes the request process in question to fail, causing the
-relevant call to diameter:call/4
-to return {error, failure} (unless the detach option was
-specified).
-In particular, there is no handle_error/4 callback in this
+decoded causes the request process to fail, causing
+diameter:call/4
+to return {error, failure} unless the detach option was
+specified.
+In particular, there is no handle_error/4 callback in this
case.
-Application configuration may change this behaviour as described for
-diameter:start_service/2.
+The diameter:application_opt()
+answer_errors can be set to change this behaviour.
@@ -474,21 +507,20 @@ marker="diameter#start_service">diameter:start_service/2.
-Invoked when an error occurs before an answer message is received from
-a peer in response to an outgoing request.
-The return value is returned from the call to diameter:call/4 for which the
-callback takes place (unless the detach option was
-specified).
+Invoked when an error occurs before an answer message is received
+in response to an outgoing request.
+The return value is returned from diameter:call/4 unless the
+detach option was specified.
Reason timeout indicates that an answer message has not been
-received within the required time.
+received within the time specified with the corresponding diameter:call_opt().
Reason failover indicates
that the transport connection to the peer to which the request has
-been sent has been lost but that not alternate node was available,
-possibly because a pick_peer/4
-callback returned false.
+been sent has become unavailable and that not alternate peer was
+not selected.
@@ -504,8 +536,7 @@ callback returned false.
Action = Reply
| {relay, [Opt]}
| discard
- | {eval, Action, PostF}
- | {eval_packet, Action, PostF}
+ | {eval|eval_packet, Action, PostF}
Reply = {reply, message()}
| {protocol_error, 3000..3999}
Opt = diameter:call_opt()
@@ -624,7 +655,8 @@ causes the request to be answered with 3002 (DIAMETER_UNABLE_TO_DELIVER).
discard
-
-Discard the request.
+Discard the request.
+No answer message is sent to the peer.
{eval, Action, PostF}
@@ -640,7 +672,7 @@ The return value is ignored.
Like eval but evaluate PostF on any encoded
#diameter_packet{} prior to transmission, the bin field
-of this record containing the encoded binary.
+containing the encoded binary.
The return value is ignored.
--
cgit v1.2.3
From 3e936e07da7f0c949d2ba08ee7a8d3d533dc27db Mon Sep 17 00:00:00 2001
From: Anders Svensson
Date: Tue, 9 Oct 2012 02:07:42 +0200
Subject: Use packet callbacks in traffic suite
---
lib/diameter/test/diameter_traffic_SUITE.erl | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl
index 669918f757..dd07679764 100644
--- a/lib/diameter/test/diameter_traffic_SUITE.erl
+++ b/lib/diameter/test/diameter_traffic_SUITE.erl
@@ -624,7 +624,10 @@ prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, Name) ->
{send, prepare(Pkt, Caps, Name)}.
prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, send_detach, _) ->
- {send, prepare(Pkt, Caps)}.
+ {eval_packet, {send, prepare(Pkt, Caps)}, [fun log/2, detach]}.
+
+log(#diameter_packet{} = P, T) ->
+ io:format("~p: ~p~n", [T,P]).
prepare(Pkt, Caps, send_unsupported) ->
Req = prepare(Pkt, Caps),
@@ -738,7 +741,7 @@ handle_request(#diameter_packet{msg = M}, ?SERVER, {_Ref, Caps}) ->
request(#diameter_base_accounting_ACR{'Accounting-Record-Number' = 0},
_) ->
- {protocol_error, ?INVALID_AVP_BITS};
+ {eval_packet, {protocol_error, ?INVALID_AVP_BITS}, [fun log/2, invalid]};
request(#diameter_base_accounting_ACR{'Session-Id' = SId,
'Accounting-Record-Type' = RT,
--
cgit v1.2.3