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