diff options
author | Anders Svensson <[email protected]> | 2017-04-30 10:30:59 +0200 |
---|---|---|
committer | Anders Svensson <[email protected]> | 2017-06-13 13:50:07 +0200 |
commit | 84dbccad048374e3eb1ec7372fd177eba022d108 (patch) | |
tree | ec209217c96f23d9327e08da35f721b08a23cc38 /lib/diameter/src | |
parent | 9d08b9d8d9d500259eeb808af19f9cf3d8d79fdf (diff) | |
download | otp-84dbccad048374e3eb1ec7372fd177eba022d108.tar.gz otp-84dbccad048374e3eb1ec7372fd177eba022d108.tar.bz2 otp-84dbccad048374e3eb1ec7372fd177eba022d108.zip |
Add diameter_codec option ordered_encode
To allow list-valued messaged to be encoded in the specified order,
instead of in the dictionary order by first converting the list to a
record. This is not yet exposed in configuration.
Diffstat (limited to 'lib/diameter/src')
-rw-r--r-- | lib/diameter/src/base/diameter_codec.erl | 30 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_peer_fsm.erl | 37 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_traffic.erl | 172 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_watchdog.erl | 30 |
4 files changed, 148 insertions, 121 deletions
diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl index 52eb10b8c2..a4d816db4e 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -21,6 +21,7 @@ -module(diameter_codec). -export([encode/2, + encode/3, decode/3, decode/4, collect_avps/1, @@ -67,13 +68,22 @@ %%% # encode/2 %%% --------------------------------------------------------------------------- --spec encode(module(), Msg :: term()) +encode(Mod, Msg) -> + encode(Mod, #{ordered_encode => true}, Msg). + +%%% --------------------------------------------------------------------------- +%%% # encode/3 +%%% --------------------------------------------------------------------------- + +-spec encode(module(), + map(), + Msg :: term()) -> #diameter_packet{} | no_return(). -encode(Mod, #diameter_packet{} = Pkt) -> +encode(Mod, Opts, #diameter_packet{} = Pkt) -> try - encode(Mod, _Opts = [], Pkt) + enc(Mod, Opts, Pkt) catch exit: {Reason, Stack, #diameter_header{} = H} = T -> %% Exit with a header in the reason to let the caller @@ -86,18 +96,18 @@ encode(Mod, #diameter_packet{} = Pkt) -> exit({?MODULE, encode, T}) end; -encode(Mod, Msg) -> +encode(Mod, Opts, Msg) -> Seq = diameter_session:sequence(), Hdr = #diameter_header{version = ?DIAMETER_VERSION, end_to_end_id = Seq, hop_by_hop_id = Seq}, - encode(Mod, #diameter_packet{header = Hdr, - msg = Msg}). + encode(Mod, Opts, #diameter_packet{header = Hdr, + msg = Msg}). -%% encode/3 +%% enc/3 -encode(_, Opts, #diameter_packet{msg = [#diameter_header{} = Hdr | As]} - = Pkt) -> +enc(_, Opts, #diameter_packet{msg = [#diameter_header{} = Hdr | As]} + = Pkt) -> try encode_avps(reorder(As), Opts) of Avps -> Bin = list_to_binary(Avps), @@ -126,7 +136,7 @@ encode(_, Opts, #diameter_packet{msg = [#diameter_header{} = Hdr | As]} exit({Reason, diameter_lib:get_stacktrace(), Hdr}) end; -encode(Mod, Opts, #diameter_packet{header = Hdr0, msg = Msg} = Pkt) -> +enc(Mod, Opts, #diameter_packet{header = Hdr0, msg = Msg} = Pkt) -> MsgName = rec2msg(Mod, Msg), {Code, Flags, Aid} = msg_header(Mod, MsgName, Hdr0), diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl index e0bcc565e7..a5dab45684 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.erl @@ -130,7 +130,8 @@ %% diameter:call/4. codec :: #{string_decode := boolean(), strict_mbit := boolean(), - rfc := 3588 | 6733}, + rfc := 3588 | 6733, + ordered_encode := false}, strict :: boolean(), ack = false :: boolean(), length_errors :: exit | handle | discard, @@ -252,7 +253,11 @@ i({Ack, WPid, {M, Ref} = T, Opts, {SvcOpts, Nodes, Dict0, Svc}}) -> length_errors = LengthErr, strict = Strictness, incoming_maxlen = Maxlen, - codec = maps:with([string_decode, strict_mbit, rfc], SvcOpts)}. + codec = maps:with([string_decode, + strict_mbit, + rfc, + ordered_encode], + SvcOpts#{ordered_encode => false})}. %% The transport returns its local ip addresses so that different %% transports on the same service can use different local addresses. %% The local addresses are put into Host-IP-Address avps here when @@ -592,7 +597,8 @@ send_CER(#state{state = {'Wait-Conn-Ack', Tmo}, mode = {connect, Remote}, service = #diameter_service{capabilities = LCaps}, transport = TPid, - dictionary = Dict} + dictionary = Dict, + codec = Opts} = S) -> OH = LCaps#diameter_caps.origin_host, req_send_CER(OH, Remote) @@ -602,7 +608,7 @@ send_CER(#state{state = {'Wait-Conn-Ack', Tmo}, #diameter_packet{header = #diameter_header{end_to_end_id = Eid, hop_by_hop_id = Hid}} = Pkt - = encode(CER, Dict), + = encode(CER, Opts, Dict), incr(send, Pkt, Dict), send(TPid, Pkt), ?LOG(send, 'CER'), @@ -631,15 +637,15 @@ build_CER(#state{service = #diameter_service{capabilities = LCaps}, {ok, CER} = diameter_capx:build_CER(LCaps, Dict), CER. -%% encode/2 +%% encode/3 -encode(Rec, Dict) -> +encode(Rec, Opts, Dict) -> Seq = diameter_session:sequence({_,_} = getr(?SEQUENCE_KEY)), Hdr = #diameter_header{version = ?DIAMETER_VERSION, end_to_end_id = Seq, hop_by_hop_id = Seq}, - diameter_codec:encode(Dict, #diameter_packet{header = Hdr, - msg = Rec}). + diameter_codec:encode(Dict, Opts, #diameter_packet{header = Hdr, + msg = Rec}). %% incoming/2 @@ -915,7 +921,10 @@ handle_request(Name, %% send_answer/3 -send_answer(Type, ReqPkt, #state{transport = TPid, dictionary = Dict} = S) -> +send_answer(Type, ReqPkt, #state{transport = TPid, + dictionary = Dict, + codec = Opts} + = S) -> incr_error(recv, ReqPkt, Dict), #diameter_packet{header = H, @@ -934,7 +943,7 @@ send_answer(Type, ReqPkt, #state{transport = TPid, dictionary = Dict} = S) -> msg = Msg, transport_data = TD}, - AnsPkt = diameter_codec:encode(Dict, Pkt), + AnsPkt = diameter_codec:encode(Dict, Opts, Pkt), incr(send, AnsPkt, Dict), incr_rc(send, AnsPkt, Dict), @@ -1358,8 +1367,9 @@ dpr([], [Reason | _], S) -> -record(opts, {cause, timeout}). -send_dpr(Reason, Opts, #state{dictionary = Dict, - service = #diameter_service{capabilities = Caps}} +send_dpr(Reason, DprOpts, #state{dictionary = Dict, + service = #diameter_service{capabilities = Caps}, + codec = Opts} = S) -> #opts{cause = Cause, timeout = Tmo} = lists:foldl(fun opt/2, @@ -1368,7 +1378,7 @@ send_dpr(Reason, Opts, #state{dictionary = Dict, _ -> ?REBOOT end, timeout = dpa_timeout()}, - Opts), + DprOpts), #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}} = Caps, @@ -1376,6 +1386,7 @@ send_dpr(Reason, Opts, #state{dictionary = Dict, Pkt = encode(['DPR', {'Origin-Host', OH}, {'Origin-Realm', OR}, {'Disconnect-Cause', Cause}], + Opts, Dict), send_dpr(false, Pkt, Tmo, S). diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index cbccf5d006..daba440586 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -103,6 +103,7 @@ make_recvdata([SvcName, PeerT, Apps, SvcOpts | _]) -> sequence = Mask, codec = maps:with([string_decode, strict_mbit, + ordered_encode, incoming_maxlen], SvcOpts)}}. @@ -479,11 +480,12 @@ request_cb(T, App, _, _) -> %% send_A/4 -send_A({Caps, Pkt}, TPid, Dict0, _RecvData) -> %% unsupported application +send_A({Caps, Pkt}, TPid, Dict0, RecvData) -> %% unsupported application #diameter_packet{errors = [RC|_]} = Pkt, send_A(answer_message(RC, Caps, Dict0, Pkt), TPid, {Dict0, Dict0}, + RecvData, Pkt, [], []); @@ -492,6 +494,7 @@ send_A({Caps, Pkt, App, {T, EvalPktFs, EvalFs}}, TPid, Dict0, RecvData) -> send_A(answer(T, Caps, Pkt, App, Dict0, RecvData), TPid, {App#diameter_app.dictionary, Dict0}, + RecvData, Pkt, EvalPktFs, EvalFs); @@ -499,10 +502,17 @@ send_A({Caps, Pkt, App, {T, EvalPktFs, EvalFs}}, TPid, Dict0, RecvData) -> send_A(_, _, _, _) -> ok. -%% send_A/6 +%% send_A/7 -send_A(T, TPid, {AppDict, Dict0} = DictT0, ReqPkt, EvalPktFs, EvalFs) -> - {MsgDict, Pkt} = reply(T, TPid, DictT0, EvalPktFs, ReqPkt), +send_A(T, + TPid, + {AppDict, Dict0} + = DictT0, + RecvData, + ReqPkt, + EvalPktFs, + EvalFs) -> + {MsgDict, Pkt} = reply(T, TPid, DictT0, RecvData, EvalPktFs, ReqPkt), incr(send, Pkt, TPid, AppDict), incr_rc(send, Pkt, TPid, {MsgDict, AppDict, Dict0}), %% count outgoing send(TPid, Pkt, _Route = self()), @@ -670,32 +680,43 @@ is_loop(Code, Vid, OH, Dict0, [_ | Avps]) is_loop(Code, Vid, OH, Dict0, Avps) -> is_loop(Code, Vid, list_to_binary(OH), Dict0, Avps). -%% reply/5 +%% reply/6 %% Local answer ... -reply({MsgDict, Ans}, TPid, {AppDict, Dict0}, Fs, ReqPkt) -> - local(Ans, TPid, {MsgDict, AppDict, Dict0}, Fs, ReqPkt); +reply({MsgDict, Ans}, TPid, {AppDict, Dict0}, RecvData, Fs, ReqPkt) -> + local(Ans, TPid, {MsgDict, AppDict, Dict0}, RecvData, Fs, ReqPkt); %% ... or relayed. -reply(#diameter_packet{} = Pkt, _TPid, {AppDict, Dict0}, Fs, _ReqPkt) -> +reply(#diameter_packet{} = Pkt, _TPid, {AppDict, Dict0}, _, Fs, _ReqPkt) -> eval_packet(Pkt, Fs), {msg_dict(AppDict, Dict0, Pkt), Pkt}. -%% local/5 +%% local/6 %% %% Send a locally originating reply. %% Skip the setting of Result-Code and Failed-AVP's below. This is %% undocumented and shouldn't be relied on. -local([Msg], TPid, DictT, Fs, ReqPkt) +local([Msg], TPid, DictT, RecvData, Fs, ReqPkt) when is_list(Msg); is_tuple(Msg) -> - local(Msg, TPid, DictT, Fs, ReqPkt#diameter_packet{errors = []}); - -local(Msg, TPid, {MsgDict, AppDict, Dict0}, Fs, ReqPkt) -> + local(Msg, + TPid, + DictT, + RecvData, + Fs, + ReqPkt#diameter_packet{errors = []}); + +local(Msg, + TPid, + {MsgDict, AppDict, Dict0}, + #recvdata{codec = Opts}, + Fs, + ReqPkt) -> Pkt = encode({MsgDict, AppDict}, TPid, make_answer_packet(Msg, ReqPkt, MsgDict, Dict0), + Opts, Fs), {MsgDict, Pkt}. @@ -1344,7 +1365,11 @@ send_request({{TPid, _Caps} = TC, App} case prepare(cb(App, prepare_request, [Pkt, SvcName, TC]), []) of [Msg | Fs] -> ReqPkt = make_request_packet(Msg, Pkt), - EncPkt = encode(App#diameter_app.dictionary, TPid, ReqPkt, Fs), + EncPkt = encode(App#diameter_app.dictionary, + TPid, + ReqPkt, + SvcOpts, + Fs), T = send_R(ReqPkt, EncPkt, Transport, CallOpts, Caller, SvcName), Ans = recv_answer(SvcName, App, CallOpts, T), handle_answer(SvcName, SvcOpts, App, Ans); @@ -1504,7 +1529,8 @@ send_R(ReqPkt, %% recv_answer/4 -recv_answer(SvcName, App, CallOpts, {TRef, MRef, #request{ref = Ref} = Req}) -> +recv_answer(SvcName, App, CallOpts, {TRef, MRef, #request{ref = Ref} + = Req}) -> %% Matching on TRef below ensures we ignore messages that pertain %% to a previous transport prior to failover. The answer message %% includes the pid of the transport on which it was received, @@ -1611,11 +1637,32 @@ handle_error(Hdr, SvcName, discard) -> %% resend_request/4 -resend_request({{_, App} = Transport, _}, Req, CallOpts, SvcName) -> - try retransmit(Transport, Req, SvcName, CallOpts#options.timeout) of - T -> recv_answer(SvcName, App, CallOpts, T) - catch - ?FAILURE(Reason) -> {error, Req, Reason} +resend_request({{{TPid, _Caps} = TC, App}, SvcOpts}, + Req0, + #options{timeout = Timeout} + = CallOpts, + SvcName) -> + case + undefined == get(TPid) + andalso prepare_retransmit(TC, App, Req0, SvcName) + of + [ReqPkt | Fs] -> + AppDict = App#diameter_app.dictionary, + EncPkt = encode(AppDict, TPid, ReqPkt, SvcOpts, Fs), + Req = Req0#request{peer = TC, + packet = ReqPkt}, + ?LOG(retransmission, EncPkt#diameter_packet.header), + incr(TPid, {msg_id(EncPkt, AppDict), send, retransmission}), + {TRef, MRef} = zend_requezt(TPid, EncPkt, Req, SvcName, Timeout), + recv_answer(SvcName, App, CallOpts, {TRef, MRef, Req}); + false -> + {error, Req0, timeout}; + {discard, Reason} -> + {error, Req0, Reason}; + discard -> + {error, Req0, discarded}; + {error, T} -> + ?ERROR({invalid_return, T, prepare_retransmit, App}) end; resend_request(_, Req, _, _) -> %% no alternate peer @@ -1651,29 +1698,29 @@ msg(#diameter_packet{msg = undefined, bin = Bin}) -> msg(#diameter_packet{msg = Msg}) -> Msg. -%% encode/4 +%% encode/5 -encode(Dict, TPid, Pkt, Fs) -> - P = encode(Dict, TPid, Pkt), +encode(Dict, TPid, Pkt, Opts, Fs) -> + P = encode(Dict, TPid, Opts, Pkt), eval_packet(P, Fs), P. -%% encode/2 +%% encode/4 %% Note that prepare_request can return a diameter_packet containing a %% header or transport_data. Even allow the returned record to contain %% an encoded binary. This isn't the usual case and doesn't properly %% support retransmission but is useful for test. -encode(Dict, TPid, Pkt) +encode(Dict, TPid, Opts, Pkt) when is_atom(Dict) -> - encode({Dict, Dict}, TPid, Pkt); + encode({Dict, Dict}, TPid, Opts, Pkt); %% A message to be encoded. -encode(DictT, TPid, #diameter_packet{bin = undefined} = Pkt) -> +encode(DictT, TPid, Opts, #diameter_packet{bin = undefined} = Pkt) -> {Dict, AppDict} = DictT, try - diameter_codec:encode(Dict, Pkt) + diameter_codec:encode(Dict, Opts, Pkt) catch exit: {diameter_codec, encode, T} = Reason -> incr_error(send, T, TPid, AppDict), @@ -1681,7 +1728,7 @@ encode(DictT, TPid, #diameter_packet{bin = undefined} = Pkt) -> end; %% An encoded binary: just send. -encode(_, _, #diameter_packet{} = Pkt) -> +encode(_, _, _, #diameter_packet{} = Pkt) -> Pkt. %% zend_requezt/5 @@ -1752,70 +1799,21 @@ recv(TPid, Pid, TRef, {LocalTRef, MRef}) -> send(Pid, Pkt, Route) -> Pid ! {send, Pkt, Route}. -%% retransmit/4 +%% prepare_retransmit/4 -retransmit({{TPid, _Caps} = TC, App} - = Transport, - #request{packet = ReqPkt} - = Req, - SvcName, - Timeout) -> - undefined == get(TPid) %% Don't failover to a peer we've - orelse ?THROW(timeout), %% already sent to. +prepare_retransmit({_TPid, _Caps} = TC, App, Req, SvcName) -> + Pkt = make_retransmit_packet(Req#request.packet), - Pkt = make_retransmit_packet(ReqPkt), + case prepare(cb(App, prepare_retransmit, [Pkt, SvcName, TC]), []) of + [Msg | Fs] -> + [make_request_packet(Msg, Pkt) | Fs]; + No -> + No + end. - retransmit(cb(App, prepare_retransmit, [Pkt, SvcName, TC]), - Transport, - Req#request{packet = Pkt}, - SvcName, - Timeout, - []). %% When sending a binary, it's up to prepare_retransmit to modify it %% accordingly. -retransmit({send, Msg}, - Transport, - #request{packet = Pkt} - = Req, - SvcName, - Timeout, - Fs) -> - resend_request(make_request_packet(Msg, Pkt), - Transport, - Req, - SvcName, - Timeout, - Fs); - -retransmit({discard, Reason}, _, _, _, _, _) -> - ?THROW(Reason); - -retransmit(discard, _, _, _, _, _) -> - ?THROW(discarded); - -retransmit({eval_packet, RC, F}, Transport, Req, SvcName, Timeout, Fs) -> - retransmit(RC, Transport, Req, SvcName, Timeout, [F|Fs]); - -retransmit(T, {_, App}, _, _, _, _) -> - ?ERROR({invalid_return, T, prepare_retransmit, App}). - -resend_request(ReqPkt, - {{TPid, _Caps} = TC, #diameter_app{dictionary = AppDict}}, - Req0, - SvcName, - Tmo, - Fs) -> - EncPkt = encode(AppDict, TPid, ReqPkt, Fs), - - Req = Req0#request{peer = TC, - packet = ReqPkt}, - - ?LOG(retransmission, EncPkt#diameter_packet.header), - incr(TPid, {msg_id(EncPkt, AppDict), send, retransmission}), - {TRef, MRef} = zend_requezt(TPid, EncPkt, Req, SvcName, Tmo), - {TRef, MRef, Req}. - %% peer_monitor/2 peer_monitor(TPid, TRef) -> diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl index b827925400..a63425d92a 100644 --- a/lib/diameter/src/base/diameter_watchdog.erl +++ b/lib/diameter/src/base/diameter_watchdog.erl @@ -76,6 +76,7 @@ strict_mbit := boolean(), failed_avp := false, rfc := 3588 | 6733, + ordered_encode := false, incoming_maxlen := diameter:message_length()}, shutdown = false :: boolean()}). @@ -134,7 +135,12 @@ i({Ack, T, Pid, {Opts, putr(restart, {T, Opts, Svc, SvcOpts}), %% save seeing it in trace putr(dwr, dwr(Caps)), %% Nodes = restrict_nodes(Restrict), - CodecKeys = [string_decode, strict_mbit, incoming_maxlen, spawn_opt, rfc], + CodecKeys = [string_decode, + strict_mbit, + incoming_maxlen, + spawn_opt, + rfc, + ordered_encode], #watchdog{parent = Pid, transport = start(T, Opts, SvcOpts, Nodes, Dict0, Svc), @@ -149,7 +155,8 @@ i({Ack, T, Pid, {Opts, suspect => 1, okay => 3}, Opts)), - codec = maps:with(CodecKeys, SvcOpts#{string_decode := false})}. + codec = maps:with(CodecKeys, SvcOpts#{string_decode := false, + ordered_encode => false})}. wait(Ref, Pid) -> receive @@ -502,9 +509,9 @@ getr(Key) -> eraser(Key) -> erase({?MODULE, Key}). -%% encode/3 +%% encode/4 -encode(dwr = M, Dict0, Mask) -> +encode(dwr = M, Dict0, Opts, Mask) -> Msg = getr(M), Seq = diameter_session:sequence(Mask), Hdr = #diameter_header{version = ?DIAMETER_VERSION, @@ -512,10 +519,10 @@ encode(dwr = M, Dict0, Mask) -> hop_by_hop_id = Seq}, Pkt = #diameter_packet{header = Hdr, msg = Msg}, - diameter_codec:encode(Dict0, Pkt); + diameter_codec:encode(Dict0, Opts, Pkt); -encode(dwa, Dict0, #diameter_packet{header = H, transport_data = TD} - = ReqPkt) -> +encode(dwa, Dict0, Opts, #diameter_packet{header = H, transport_data = TD} + = ReqPkt) -> AnsPkt = #diameter_packet{header = H#diameter_header{is_request = false, is_error = undefined, @@ -523,7 +530,7 @@ encode(dwa, Dict0, #diameter_packet{header = H, transport_data = TD} msg = dwa(ReqPkt), transport_data = TD}, - diameter_codec:encode(Dict0, AnsPkt). + diameter_codec:encode(Dict0, Opts, AnsPkt). %% okay/3 @@ -593,9 +600,10 @@ tw({M,F,A}) -> send_watchdog(#watchdog{pending = false, transport = TPid, dictionary = Dict0, - config = #{sequence := Mask}} + config = #{sequence := Mask}, + codec = Opts} = S) -> - #diameter_packet{bin = Bin} = EPkt = encode(dwr, Dict0, Mask), + #diameter_packet{bin = Bin} = EPkt = encode(dwr, Dict0, Opts, Mask), diameter_traffic:incr(send, EPkt, TPid, Dict0), send(TPid, {send, Bin}), ?LOG(send, 'DWR'), @@ -628,7 +636,7 @@ rcv('DWR', Pkt, #watchdog{transport = TPid, transport_data = T, bin = Bin} = EPkt - = encode(dwa, Dict0, Pkt), + = encode(dwa, Dict0, Opts, Pkt), diameter_traffic:incr(send, EPkt, TPid, Dict0), diameter_traffic:incr_rc(send, EPkt, TPid, Dict0), |