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 dictionary 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.
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 connectivity 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.
Note!
The arities given for the the callback functions here 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, for the call-specific callbacks, any extra arguments passed to diameter:call/4.
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 specified in the relevant 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 (since relays agents must handle arbitrary request): 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. Fields of a packet() record 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.
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.
Functions
Mod:peer_up(SvcName, Peer, State) -> NewState
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
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(Candidates, Reserved, SvcName, State) -> {ok, Peer} | {Peer, NewState} | false
Candidates = [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, and only
those which have indicated support for the Diameter application in
question.
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]}}
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
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
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.
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.
A returned length
, cmd_code
or application_id
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
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
argument packet() is as returned by the initial
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
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 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.
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} (unless the
.
In particular, there is no detach
option was
specified)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
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 (unless the detach
option was
specified).
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
Packet = packet()
SvcName = term()
Peer = peer()
Action = Reply | {relay, Opts} | discard | {eval, Action, PostF}
Reply = {reply, message()} | {protocol_error, 3000..3999}
Opts = diameter:call_opts()
PostF = diameter:evaluable()
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 request message, the selected module being the one whose corresponding dictionary declares itself as defining either the application in question or the Relay application.
The argument packet() has the following signature.
#diameter_packet{header = #diameter_header{},
avps = [#diameter_avp{}],
msg = record() | undefined,
errors = ['Unsigned32'() | {'Unsigned32'(), #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 Result-Code's identifying errors
that were encountered in decoding the request.
In this case diameter will set both Result-Code and
Failed-AVP AVP's in a returned
answer message() before sending it to the peer:
the returned message() need only set any other required AVP's.
Note that the errors detected by diameter are all of the 5xxx series
(Permanent Failures).
The errors
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.
{reply, message()}
Send the specified answer message to the peer.
{protocol_error, 3000..3999}
Send an answer message to the peer containing the specified protocol error. Equivalent to
{reply, ['answer-message' | Avps]
where Avps
sets the Origin-Host, Origin-Realm, the specified
Result-Code and (if the request sent one) Session-Id AVP's.
Note that 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
tuple
will cause the request process in question to fail.
{relay, Opts}
Relay a request to another peer in the role of a Diameter relay agent. If a routing loop is detected then the request is answered with 3005 (DIAMETER_LOOP_DETECTED). Otherwise a Route-Record AVP (containing the sending peer's Origin-Host) is added to the request and pick_peer/4 and subsequent callbacks take place just as if diameter:call/4 had been called explicitly. The End-to-End Identifier of the incoming request is preserved in the header of the relayed request.
The returned Opts
should not specify detach
.
A subsequent handle_answer/4
callback for the relayed request must return its first
argument, the diameter_packet
record containing the answer
message.
Note that the extra
option can be specified to supply arguments
that can distinguish the relay case from others if so desired.
Any other return value (for example, from a
handle_error/4 callback)
causes the request to be answered with 3002 (DIAMETER_UNABLE_TO_DELIVER).
discard
Discard the request.
{eval, Action, PostF}
Handle the request as if Action
has been returned and then
evaluate PostF
in the request process.
Note that protocol errors detected by diameter will result in an
answer message without handle_request/3
being invoked.