aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter/doc/src/diameter_app.xml
blob: c2fecce768e978ba94918d7ec6b1945f67d92826 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
<?xml version="1.0" encoding="latin1" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">

<erlref>
<header>

<copyright>
<year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
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.

</legalnotice>

<title>diameter_app(3)</title>
<prepared>Anders Svensson</prepared>
<responsible></responsible>
<docno></docno>
<approved></approved>
<checked></checked>
<date></date>
<rev>%REV%</rev>
<file>diameter_app.xml</file>

</header>

<module>diameter_app</module>
<modulesummary>
Callback module of a Diameter application.</modulesummary>

<description>

<p>
A diameter service as started by <seealso
marker="diameter#start_service">diameter:start_service/2</seealso>
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
<seealso marker="diameter_dict">diameter_dict(4)</seealso>
while the callback module is documented here.
The callback module implements the Diameter application-specific
functionality of a service.</p>

<note>
<p>
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 <seealso
marker="diameter#start_service">diameter:start_service/2</seealso>
and, except for peer_up, peer_down and handle_request, any extra
arguments passed to <seealso
marker="diameter#call">diameter:call/4</seealso>.</p>
</note>

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

<list>
<item>
<p>
<seealso marker="#peer_up">peer_up/3</seealso> and
<seealso marker="#peer_down">peer_down/3</seealso> signal the attainment
or loss of communicativity with a Diameter peer.</p>
</item>

<item>
<p>
<seealso marker="#pick_peer">pick_peer/4</seealso>,
<seealso marker="#prepare_request">prepare_request/3</seealso>,
<seealso marker="#prepare_retransmit">prepare_retransmit/3</seealso>,
<seealso marker="#handle_answer">handle_answer/4</seealso>
and <seealso marker="#handle_error">handle_error/4</seealso> are (or may
be) called as a consequence of a call to <seealso 
marker="diameter#call">diameter:call/4</seealso> to send an outgoing
Diameter request message.</p>
</item>

<item>
<p>
<seealso marker="#handle_request">handle_request/3</seealso>
is called in response to an incoming Diameter request message.</p>
</item>

</list>

</description>

<!-- ===================================================================== -->
<!-- ===================================================================== -->

<section>
<title>DATA TYPES</title>

<taglist>

<tag><c>capabilities() = #diameter_caps{}</c></tag>
<item>
<p>
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.</p>
</item>

<tag><c>message() = record() | list()</c></tag>
<item>
<p>
The representation of a Diameter message as passed to
<seealso marker="diameter#call">diameter:call/4</seealso>.
The record representation is as outlined in
<seealso
marker="diameter_dict#MESSAGE_RECORDS">diameter_dict(4)</seealso>:
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
<c>{FieldName, FieldValue}</c> pairs.</p>

<p>
A third representation allows a message to be specified as a list
whose head is a <c>diameter_header</c> record and whose tail is a list
of <c>diameter_avp</c> records.
This representation is used by diameter itself when relaying requests
as directed by the return value of a
<seealso marker="#handle_request">handle_request/3</seealso>
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.</p>

</item>

<tag><c>packet() = #diameter_packet{}</c></tag>
<item>
<p>
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.</p>
</item>

<tag><c>peer_ref() = term()</c></tag>
<item>
<p>
A term identifying a transport connection with a Diameter peer.
Should be treated opaquely.</p>
</item>

<tag><c>peer() = {peer_ref(), capabilities()}</c></tag>
<item>
<p>
A tuple representing a Diameter peer connection.</p>
</item>

<tag><c>service_name() = term()</c></tag>
<item>
<p>
The service supporting the Diameter application.
Specified to <seealso
marker="diameter#start_service">diameter:start_service/2</seealso>
when starting the service.</p>
</item>

<tag><c>state() = term()</c></tag>
<item>
<p>
The state maintained by the application callback functions
<seealso marker="#peer_up">peer_up/3</seealso>,
<seealso marker="#peer_down">peer_down/3</seealso> and (optionally)
<seealso marker="#pick_peer">pick_peer/4</seealso>.
The initial state is configured in the call to
<seealso
marker="diameter#start_service">diameter:start_service/2</seealso>
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.</p>
</item>

</taglist>

<marker id="peer_up"/>
</section>

<!-- ===================================================================== -->
<!-- ===================================================================== -->

<funcs>

<func>
<name>Mod:peer_up(SvcName, Peer, State) -> NewState</name>
<fsummary>Invoked when a transport connection has been established</fsummary>
<type>
<v>SvcName = service_name()</v>
<v>Peer    = peer()</v>
<v>State   = NewState = state()</v>
</type>
<desc>
<p>
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.</p>

<marker id="peer_down"/>
</desc>
</func>

<func>
<name>Mod:peer_down(SvcName, Peer, State) -> NewState</name>
<fsummary>Invoked when a transport connection has been lost.</fsummary>
<type>
<v>SvcName = service_name()</v>
<v>Peer    = peer()</v>
<v>State   = NewState = state()</v>
</type>
<desc>
<p>
Invoked when a transport connection has been lost following a previous
call to <seealso marker="peer_up">peer_up/3</seealso>.</p>

<marker id="pick_peer"/>
</desc>
</func>

<func>
<name>Mod:pick_peer(Cands, Reserved, SvcName, State)
      -> {ok, Peer} | {Peer, NewState} | false</name>
<fsummary>Select a target peer for an outgoing request.</fsummary>
<type>
<v>Cands = [Peer]</v>
<v>Peer = peer() | false</v>
<v>SvcName = service_name()</v>
<v>State = NewState = state()</v>
</type>
<desc>
<p>
Invoked as a consequence of a call to <seealso
marker="diameter#call">diameter:call/4</seealso> 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 <c>call_mutates_state</c> set to
<c>true</c>, as documented for <seealso
marker="diameter#start_service">diameter:start_service/2</seealso>.</p>

<p>
The candidate peers list will only include those
which are selected by any <c>filter</c> option specified in the call to
<seealso marker="diameter#call">diameter:call/4</seealso>.</p>
<!--
The local candidates are those whose transport process is executing on
the local Erlang node, the remote list those that are available on
other nodes.</p> -->

<p>
The return values <c>false</c> and <c>{false, State}</c> are
equivalent when callback state is mutable, as are
<c>{ok, Peer}</c> and <c>{Peer, State}</c>.
Returning a peer as <c>false</c> causes <c>{error, no_connection}</c>
to be returned from <seealso marker="diameter#call">diameter:call/4</seealso>.
Returning a peer() from an initial pick_peer/4 callback will result in a
<seealso marker="#prepare_request">prepare_request/3</seealso> callback
followed by either <seealso
marker="#handle_answer">handle_answer/4</seealso>
or <seealso marker="#handle_error">handle_error/4</seealso> 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 <seealso
marker="#pick_peer">pick_peer/4</seealso> callback takes place to
select an alternate peer.</p>

<p>
Note that there is no guarantee that a <seealso
marker="#pick_peer">pick_peer/4</seealso> callback to select
an alternate peer will be followed by any additional callbacks, only
that the initial <seealso
marker="#pick_peer">pick_peer/4</seealso> will be, since a
retransmission to an alternate peer is abandoned if an answer is
received from a previously selected peer.</p>

<marker id="prepare_request"/>
</desc>

</func>

<func>
<name>Mod:prepare_request(Packet, SvcName, Peer) -> Action</name>
<fsummary>Return a request for encoding and transport.</fsummary>
<type>
<v>Packet = packet()</v>
<v>SvcName = service_name()</v>
<v>Peer = peer()</v>
<v>Action = {send, packet() | message()} | {discard, Reason} | discard</v>
</type>
<desc>
<p>
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) <c>Destination-Host</c> and/or
<c>Destination-Realm</c> in the outgoing request, although the
callback need not be limited to this usage.
Many implementations may simply want to return <c>{send, Packet}</c></p>

<p>
A returned packet() should set the request to be encoded in its
<c>msg</c> field and can set the <c>transport_data</c> field in order
to pass information to the transport module.
Extra arguments passed to <seealso
marker="diameter#call">diameter:call/4</seealso> can be used to
communicate transport data to the callback.</p>

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

<p>
Returning <c>{discard, Reason}</c> causes the request to be aborted
and the <seealso
marker="diameter#call">diameter:call/4</seealso> for which the
callback has taken place to return <c>{error, Reason}</c>.
Returning <c>discard</c> is equivalent to returning <c>{discard,
discarded}</c>.</p>

<marker id="prepare_retransmit"/>
</desc>
</func>

<func>
<name>Mod:prepare_retransmit(Packet, SvcName, Peer) -> Result</name>
<fsummary>Return a request for encoding and retransmission.</fsummary>
<type>
<v>Packet  = packet()</v>
<v>SvcName = service_name()</v>
<v>Peer    = peer()</v>
<v>Result  = {send, packet() | message()} | {discard, Reason} | discard</v>
</type>
<desc>
<p>
Invoked to return a request for encoding and retransmission.
Has the same role as <seealso
marker="#prepare_request">prepare_request/3</seealso> in the case that
a peer connection is lost an an alternate peer selected but the
Packet passed to <c>prepare_retransmit/3</c> is as returned by
<c>prepare_request/3</c>.</p>

<p>
Returning <c>{discard, Reason}</c> causes the request to be aborted
and a <seealso
marker="#handle_error">handle_error/4</seealso> callback to
take place with <c>Reason</c> as initial argument.
Returning <c>discard</c> is equivalent to returning <c>{discard,
discarded}</c>.</p>

<marker id="handle_answer"/>
</desc>
</func>

<func>
<name>Mod:handle_answer(Packet, Request, SvcName, Peer) -> Result</name>
<fsummary>Receive an answer message from a peer.</fsummary>
<type>
<v>Packet  = packet()</v>
<v>Request = message()</v>
<v>SvcName = service_name()</v>
<v>Peer    = peer()</v>
<v>Result  = term()</v>
</type>
<desc>
<p>
Invoked when an answer message is received from a peer.
The return value is returned from the call to <seealso
marker="diameter#call">diameter:call/4</seealso> for which the
callback takes place.</p>

<p>
The decoded answer record is in the <c>msg</c> field of <c>Packet</c>,
the undecoded binary in the <c>packet</c> field.
<c>Request</c> is the outgoing request message as was returned from
<seealso marker="#prepare_request">prepare_request/3</seealso> or
<seealso marker="#prepare_retransmit">prepare_retransmit/3</seealso>
before the request was passed to the transport.</p>

<p>
For any given call to <seealso
marker="diameter#call">diameter:call/4</seealso> 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 <c>handle_answer/4</c> or <c>handle_error/4</c> is
called for any given request.</p>

<p>
By default, an incoming answer message that cannot be successfully
decoded causes the request process in question to fail, causing the
relevant call to <seealso
marker="diameter#call">diameter:call/4</seealso>
to return <c>{error, failure}</c>.
There is no <c>handle_error/4</c> callback in this case.
Application configuration may change this behaviour as described for
<seealso
marker="diameter#start_service">diameter:start_service/2</seealso>.</p>

<marker id="handle_error"/>
</desc>
</func>

<func>
<name>Mod:handle_error(Reason, Request, SvcName, Peer) -> Result</name>
<fsummary>Return an error from a outgoing request.</fsummary>
<type>
<v>Reason  = timeout | failover | term()</v>
<v>Request = message()</v>
<v>SvcName = service_name()</v>
<v>Peer    = peer()</v>
<v>Result  = term()</v>
</type>
<desc>
<p>
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 <seealso
marker="diameter#call">diameter:call/4</seealso> for which the
callback takes place.</p>

<p>
Reason <c>timeout</c> indicates that an answer message has not been
received within the required time.
Reason <c>failover</c> 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 <seealso marker="#pick_peer">pick_peer/4</seealso>
callback returned false.
</p>

<marker id="handle_request"/>
</desc>
</func>

<func>
<name>Mod:handle_request(Packet, SvcName, Peer) -> Action</name>
<fsummary>Receive an incoming request.</fsummary>
<type>
<v>Packet  = packet()</v>
<v>SvcName = term()</v>
<v>Peer    = peer()</v>
<v>Action  = Reply | NoReply | Relay | {eval, Action, ContF}</v>
<v>Reply   = {reply, message()}
           | {protocol_error, ResultCode}</v>
<v>NoReply = discard</v>
<v>Relay   = {relay, Opts}</v>
<v>Opts    = list()</v>
<v>ContF   = diameter:evaluable()</v>
<v>ResultCode = 3000..3999</v>
</type>
<desc>
<p>
Invoked when a request message is received from a peer.</p>

<p>
The application in which the callback takes place (that is, the
callback module as configured with <seealso
marker="diameter#start_service">diameter:start_service/2</seealso>)
is determined by the Application Identifier in the header of the
incoming Diameter request message, the selected module being the one
whose corresponding <seealso
marker="diameter_dict#MESSAGE_RECORDS">dictionary</seealso> 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.</p>

<p>
The packet() in which the incoming request is communicated has the
following signature.</p>

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

<p>
The <c>msg</c> field will be <c>undefined</c> only in case the request has
been received in the relay application.
Otherwise it contains the record representing the request as outlined
in <seealso
marker="diameter_dict#MESSAGE_RECORDS">diameter_dict(4)</seealso>.</p>

<p>
The <c>errors</c> field specifies any non-protocol errors that were
encountered in decoding the request and can be returned in a
<c>reply</c> 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.</p>

<p>
The <c>transport_data</c> field contains an arbitrary term passed into
diameter from the transport module in question, or the atom
<c>undefined</c> 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.</p>

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

<taglist>

<tag><c>{reply, Answer}</c></tag>
<item>
<p>
Send the specified answer message to the peer.</p>
</item>

<tag><c>{relay, Opts}</c></tag>
<item>
<p>
Relay a request to another peer.</p>
</item>

<tag><c>{protocol_error, ResultCode}</c></tag>
<item>
<p>
Send an answer message to the peer containing the specified 3xxx
protocol error.</p>

<p>
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 <c>{protocol_error, ResultCode}</c>
tuple will cause the request process in question to fail.</p>
</item>

<tag><c>discard</c></tag>
<item>
<p>
Discard the request.</p>
</item>

<tag><c>{eval, Action, ContF}</c></tag>
<item>
<p>
Handle the request as if <c>Action</c> has been returned and then
evaluate the evaluable() <c>ContF</c> in the request process.</p>
</item>

</taglist>

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

</desc>
</func>

</funcs>

</erlref>