2011 Ericsson AB. All Rights Reserved. The contents of this file are subject to the Erlang Public License, Version 1.1, (the "License"); you may not use this file except in compliance with the License. You should have received a copy of the Erlang Public License along with this software. If not, it can be retrieved online at http://www.erlang.org/. Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. diameter_app(3) Anders Svensson %REV% diameter_app.xml
diameter_app Callback module of a Diameter application.

A diameter service as started by diameter:start_service/2 configures one of more Diameter applications, each of whose configuration specifies a callback that handles messages specific to its application. The messages and AVPs of the Diameter application are defined in a specification file whose format is documented in diameter_dict(4) while the callback module is documented here. The callback module implements the Diameter application-specific functionality of a service.

The arities of the callback functions below assume no extra arguments. All functions will also be passed any extra arguments configured with the callback module itself when calling diameter:start_service/2 and, except for peer_up, peer_down and handle_request, any extra arguments passed to diameter:call/4.

A callback module must export all of the functions documented below. The functions themselves are of three distinct flavours:

peer_up/3 and peer_down/3 signal the attainment or loss of communicativity with a Diameter peer.

pick_peer/4, prepare_request/3, prepare_retransmit/3, handle_answer/4 and handle_error/4 are (or may be) called as a consequence of a call to diameter:call/4 to send an outgoing Diameter request message.

handle_request/3 is called in response to an incoming Diameter request message.

DATA TYPES capabilities() = #diameter_caps{}

A record containing the identities of the local and remote Diameter peers 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 values for the (local) host and (remote) peer. Optional or possibly multiple values are encoded as lists of values, mandatory values as the bare value.

message() = record() | list()

The representation of a Diameter message as passed to diameter:call/4. The record representation is as outlined in diameter_dict(4): a message as defined in a dictionary file is encoded as a record with one field for each component AVP. Equivalently, a message can also be encoded as a list whose head is the atom-valued message name (the record name minus any prefix in the dictionary file) and whose tail is a list of {FieldName, FieldValue} pairs.

A third representation allows a message to be specified as a list whose head is a diameter_header record and whose tail is a list of diameter_avp records. This representation is used by diameter itself when relaying requests as directed by the return value of a handle_request/3 callback. It differs from the other other two in that it bypasses the checks for messages that do not agree with their definitions in the dictionary in question: messages are sent exactly as specified.

packet() = #diameter_packet{}

A container for incoming and outgoing Diameters message that's passed through encode/decode and transport. Defined in diameter.hrl. Fields should not be altered except as documented.

peer_ref() = term()

A term identifying a transport connection with a Diameter peer. Should be treated opaquely.

peer() = {peer_ref(), capabilities()}

A tuple representing a Diameter peer connection.

service_name() = term()

The service supporting the Diameter application. Specified to diameter:start_service/2 when starting the service.

state() = term()

The state maintained by the application callback functions peer_up/3, peer_down/3 and (optionally) pick_peer/4. The initial state is configured in the call to diameter:start_service/2 that configures the application on a service. Callback functions returning a state are evaluated in a common service-specific process while those not returning state are evaluated in a request-specific process.

Mod:peer_up(SvcName, Peer, State) -> NewState Invoked when a transport connection has been established SvcName = service_name() Peer = peer() State = NewState = state()

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.

Mod:peer_down(SvcName, Peer, State) -> NewState Invoked when a transport connection has been lost. SvcName = service_name() Peer = peer() State = NewState = state()

Invoked when a transport connection has been lost following a previous call to peer_up/3.

Mod:pick_peer(Cands, Reserved, SvcName, State) -> {ok, Peer} | {Peer, NewState} | false Select a target peer for an outgoing request. Cands = [Peer] Peer = peer() | false SvcName = service_name() State = NewState = state()

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. A new application state can also be returned but only if the Diameter application in question was configured with the option call_mutates_state set to true, as documented for diameter:start_service/2.

The candidate peers list will only include those which are selected by any filter option specified in the call to diameter:call/4.

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 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 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 retransmission to an alternate peer is abandoned if an answer is received from a previously selected peer.

Mod:prepare_request(Packet, SvcName, Peer) -> Action Return a request for encoding and transport. Packet = packet() SvcName = service_name() Peer = peer() Action = {send, packet() | message()} | {discard, Reason} | discard

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. Many implementations may simply want to return {send, Packet}

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. Extra arguments passed to diameter:call/4 can be used to communicate transport data to the callback.

Any returned packet() can set the header field to a diameter_header record in order to specify values that should be preserved in the outgoing request. A specified message_length is ignored.

Returning {discard, Reason} causes the request to be aborted and the diameter:call/4 for which the callback has taken place to return {error, Reason}. Returning discard is equivalent to returning {discard, discarded}.

Mod:prepare_retransmit(Packet, SvcName, Peer) -> Result Return a request for encoding and retransmission. Packet = packet() SvcName = service_name() Peer = peer() Result = {send, packet() | message()} | {discard, Reason} | discard

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 Packet passed to prepare_retransmit/3 is as returned by prepare_request/3.

Returning {discard, Reason} causes the request to be aborted and a handle_error/4 callback to take place with Reason as initial argument. Returning discard is equivalent to returning {discard, discarded}.

Mod:handle_answer(Packet, Request, SvcName, Peer) -> Result Receive an answer message from a peer. Packet = packet() Request = message() SvcName = service_name() Peer = peer() Result = term()

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.

The decoded answer record is in the msg field of Packet, the undecoded binary in the packet field. 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.

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 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.

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}. There is no handle_error/4 callback in this case. Application configuration may change this behaviour as described for diameter:start_service/2.

Mod:handle_error(Reason, Request, SvcName, Peer) -> Result Return an error from a outgoing request. Reason = timeout | failover | term() Request = message() SvcName = service_name() Peer = peer() Result = term()

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.

Reason timeout indicates that an answer message has not been received within the required time. 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.

Mod:handle_request(Packet, SvcName, Peer) -> Action Receive an incoming request. Packet = packet() SvcName = term() Peer = peer() Action = Reply | NoReply | Relay | {eval, Action, ContF} Reply = {reply, message()} | {protocol_error, ResultCode} NoReply = discard Relay = {relay, Opts} Opts = list() ContF = diameter:evaluable() ResultCode = 3000..3999

Invoked when a request message is received from a peer.

The application in which the callback takes place (that is, the callback module as configured with diameter:start_service/2) is determined by the Application Identifier in the header of the incoming Diameter request message, the selected module being the one whose corresponding dictionary declares itself as defining the application in question, or the RFC 3588 relay application if the specific application is unsupported but the relay application has been advertised.

The packet() in which the incoming request is communicated has the following signature.

#diameter_packet{header = #diameter_header{}, avps = [#diameter_avp{}], msg = record() | undefined, errors = [integer() | {integer(), #diameter_avp{}}], bin = binary(), transport_data = term()}

The msg field will be undefined only in case the request has been received in the relay application. Otherwise it contains the record representing the request as outlined in diameter_dict(4).

The errors field specifies any non-protocol errors that were encountered in decoding the request and can be returned in a reply tuple to have diameter set the Result-Code and Failed-AVP AVP's appropriately. The list is empty if the request has been received in the relay application.

The transport_data field contains an arbitrary term passed into diameter from the transport module in question, or the atom undefined if the transport specified no data. The term is preserved in the packet() containing any answer message sent back to the transport process unless another value is explicitly specified.

The semantics of each of the possible return values are as follows. (TODO: more.)

{reply, Answer}

Send the specified answer message to the peer.

{relay, Opts}

Relay a request to another peer.

{protocol_error, ResultCode}

Send an answer message to the peer containing the specified 3xxx protocol error.

RFC 3588 mandates that only answers with a 3xxx series Result-Code (protocol errors) may set the E bit. Returning a non-3xxx value in a {protocol_error, ResultCode} tuple will cause the request process in question to fail.

discard

Discard the request.

{eval, Action, ContF}

Handle the request as if Action has been returned and then evaluate the evaluable() ContF in the request process.

Note that diameter will respond to protocol errors in an incoming request without invoking the a handle_request/3 callback.