Working with diameter applications¶
A diameter application is the highest level of abstraction provided by the
diameter
package. It automates tasks such as:
- Synchronous return of answers after sending messages
- Tracking requests that require answers
- Routing requests to any suitable peers, including load balancing
- Constructing answers from received request messages
An "application" is any instance of a subclass of
Application
. An application is
handed over to a configured node and the node will route every received request
and answer message to the application, when necessary. Protocol-level message
exchanges, i.e. CER (Capabilities-Exchange), DWR (Device-Watchdog) and DPR
(Disconnect-Peer) never reach any application, but every other message is
expected to be handled by at least one application.
If a node receives a message that no application has been configured to handle,
it will reject the message with a DIAMETER_APPLICATION_UNSUPPORTED
error.
An example of the most basic client application:
from diameter.message import Message
from diameter.message.constants import *
from diameter.node import Node
from diameter.node.application import SimpleThreadingApplication
my_app = SimpleThreadingApplication(APP_DIAMETER_BASE_ACCOUNTING,
is_acct_application=True)
node = Node("peername.gy", "realm.net")
peer = node.add_peer("aaa://ocs2.gy;transport=sctp", "realm.net",
ip_addresses=["10.16.0.8", "10.16.5.8"],
is_persistent=True)
node.add_application(my_app, [peer])
node.start()
msg = Message()
my_app.wait_for_ready()
answer = my_app.send_request(msg, timeout=10)
The add_application
call configures
also the peers that the application will use. More than one application of
same type can be added at the same time, as long as they use different peers.
The application provides a
wait_for_ready
method,
which will block until at least one of the configured peers becomes available
to receive requests.
The send_request
application instance method will block until an answer message has been
received, and then returns the message, synchronously.
The diameter
package offers three different application implementations:
Application
-
The most basic form of an application. Must be subclassed and contains two methods,
handle_request
, which must be overridden, andhandle_answer
, which may be overriden, but is usually not necessary.The
Application
class calls internallyhandle_request
for each received diameter request in the main thread. It callshandle_answer
for each answer that comes unexpected, i.e. without a request waiting for it.from diameter.message import Message from diameter.message.constants import * from diameter.node import Node from diameter.node.application import Application class MyApplication(Application): def handle_request(self, message: Message): print("Got request", message) my_app = MyApplication(APP_DIAMETER_BASE_ACCOUNTING, is_acct_application=True) node = Node("peername.gy", "realm.net") peer = node.add_peer("aaa://ocs2.gy;transport=sctp", "realm.net") node.add_application(my_app, [peer])
Warning
The request and answer are called in the main
Node
work thread and they will block the node from processing any other messages. When this class is used, the implementing party is expected to build a queue and/or threading based solution on top, which would not block the main thread. ThreadingApplication
-
A variation of application, which spawns a new thread for each incoming request, up to an optional maximum amount of threads. Must be subclassed. Also contains two methods,
handle_request
, which must be overridden, andhandle_answer
, which may be overriden, but is usually not necessary.Unlike in the base
Application
class, the threading application expects that the overriddenhandle_request
returns a valid diameter message as an answer to the received request. Failing to do so results in an automatic generation of aDIAMETER_UNABLE_TO_COMPLY
error message.from diameter.message import Message from diameter.message.constants import * from diameter.node import Node from diameter.node.application import ThreadingApplication class MyApplication(ThreadingApplication): def handle_request(self, message: Message) -> Message: print("Got request", message) answer = self.generate_answer(message) return answer my_app = MyApplication(APP_DIAMETER_BASE_ACCOUNTING, is_acct_application=True, max_threads=50) node = Node("peername.gy", "realm.net") peer = node.add_peer("aaa://ocs2.gy;transport=sctp", "realm.net") node.add_application(my_app, [peer])
SimpleThreadingApplication
-
A variation of threading application, which does not need to be subclassed and handles incoming requests only optionally. Also spawns a new thread for each incoming request, up to an optional maximum amount of threads.
In order to receive requests, the
SimpleThreadingApplication
accepts a callback method in its constructor and calls it in a separate spawned thread. Similar to the threading application, the callback function must return a valid answer, otherwise aDIAMETER_UNABLE_TO_COMPLY
error is sent back to the network.If no callback is provided, the
SimpleThreadingApplication
acts as a client only and accepts no incoming requests.from diameter.message import Message from diameter.message.constants import * from diameter.node import Node from diameter.node.application import SimpleThreadingApplication def recv_request(app: SimpleThreadingApplication, message: Message) -> Message: print("Got request", message, "through application", app) answer = app.generate_answer(message) answer.result_code = E_RESULT_CODE_DIAMETER_SUCCESS return answer my_app = SimpleThreadingApplication(APP_DIAMETER_BASE_ACCOUNTING, is_acct_application=True, max_threads=50, request_handler=recv_request) node = Node("peername.gy", "realm.net") peer = node.add_peer("aaa://ocs2.gy;transport=sctp", "realm.net") node.add_application(my_app, [peer])