Implementing your own middleware¶
A middleware class provides four handler functions, one for processing each of the four kinds of messages transports, applications and dispatchers typically send and receive (i.e. inbound user messages, outbound user messages, event messages and failure messages).
Although transport and application middleware potentially both provide the same sets of handlers, the two make use of them in slightly different ways. Inbound messages and events are published by transports but consumed by applications while outbound messages are opposite. Failure messages are not seen by applications at all and are allowed only so that certain middleware may be used on both transports and applications. Dispatchers both consume and publish all kinds of messages except failure messages.
Middleware is required to have the same interface as the
BaseMiddleware
class which is described below. Two
subclasses, TransportMiddleware
and
ApplicationMiddleware
, are provided but subclassing from
these is just a hint as to whether a piece of middleware is intended
for use on transports or applications (middleware for use on both or
for dispatchers may inherit from BaseMiddleware
). The two
subclasses provide identical interfaces and no extra functionality.
-
class
vumi.middleware.
BaseMiddleware
(name, config, worker)¶ Common middleware base class.
This is a convenient definition of and set of common functionality for middleware classes. You need not subclass this and should not instantiate this directly.
The
__init__()
method should take exactly the following options so that your class can be instantiated from configuration in a standard way:Parameters: - name (string) – Name of the middleware.
- config (dict) – Dictionary of configuraiton items.
- worker (vumi.service.Worker) – Reference to the transport or application being wrapped by this middleware.
If you are subclassing this class, you should not override
__init__()
. Custom setup should be done insetup_middleware()
instead. The config class can be overidden by replacing theconfig_class
class variable.-
CONFIG_CLASS
¶ alias of
BaseMiddlewareConfig
-
setup_middleware
()¶ Any custom setup may be done here.
Return type: Deferred or None Returns: May return a deferred that is called when setup is complete.
-
teardown_middleware
()¶ “Any custom teardown may be done here
Return type: Deferred or None Returns: May return a Deferred that is called when teardown is complete
-
handle_consume_inbound
(message, connector_name)¶ Called when an inbound transport user message is consumed.
The other methods listed below all function in the same way. Only the kind and direction of the message being processed differs.
By default, the
handle_consume_*
andhandle_publish_*
methods call theirhandle_*
equivalents.Parameters: - message (vumi.message.TransportUserMessage) – Inbound message to process.
- connector_name (string) – The name of the connector the message is being received on or sent to.
Return type: vumi.message.TransportUserMessage
Returns: The processed message.
-
handle_publish_inbound
(message, connector_name)¶ Called when an inbound transport user message is published.
-
handle_inbound
(message, connector_name)¶ Default handler for published and consumed inbound messages.
-
handle_consume_outbound
(message, connector_name)¶ Called when an outbound transport user message is consumed.
-
handle_publish_outbound
(message, connector_name)¶ Called when an outbound transport user message is published.
-
handle_outbound
(message, connector_name)¶ Default handler for published and consumed outbound messages.
-
handle_consume_event
(event, connector_name)¶ Called when a transport event is consumed.
-
handle_publish_event
(event, connector_name)¶ Called when a transport event is published.
-
handle_event
(event, connector_name)¶ Default handler for published and consumed events.
-
handle_consume_failure
(failure, connector_name)¶ Called when a failure message is consumed.
-
handle_publish_failure
(failure, connector_name)¶ Called when a failure message is published.
-
handle_failure
(failure, connector_name)¶ Called to process a failure message (
vumi.transports.failures.FailureMessage
).
Example of a simple middleware implementation from
vumi.middleware.logging
:
class LoggingMiddleware(BaseMiddleware):
"""Middleware for logging messages published and consumed by
transports and applications.
Optional configuration:
:param string log_level:
Log level from :mod:`vumi.log` to log inbound and outbound
messages and events at. Default is `info`.
:param string failure_log_level:
Log level from :mod:`vumi.log` to log failure messages at.
Default is `error`.
"""
CONFIG_CLASS = LoggingMiddlewareConfig
def setup_middleware(self):
log_level = self.config.log_level
self.message_logger = getattr(log, log_level)
failure_log_level = self.config.failure_log_level
self.failure_logger = getattr(log, failure_log_level)
def _log(self, direction, logger, msg, connector_name):
logger("Processed %s message for %s: %s" % (
direction, connector_name, msg.to_json()))
return msg
def handle_inbound(self, message, connector_name):
return self._log(
"inbound", self.message_logger, message, connector_name)
def handle_outbound(self, message, connector_name):
return self._log(
"outbound", self.message_logger, message, connector_name)
def handle_event(self, event, connector_name):
return self._log("event", self.message_logger, event, connector_name)
def handle_failure(self, failure, connector_name):
return self._log(
"failure", self.failure_logger, failure, connector_name)
How your middleware is used inside Vumi¶
While writing complex middleware, it may help to understand how a middleware class is used by Vumi transports and applications.
When a transport or application is started a list of middleware to
load is read from the configuration. An instance of each piece of
middleware is created and then setup_middleware()
is called on
each middleware object in order. If any call to
setup_middleware()
returns a Deferred
, setup will
continue after the deferred has completed.
Once the middleware has been setup it is combined into a
MiddlewareStack
. A middleware stack has two important methods
apply_consume()
and apply_publish()
The former is used
when a message is being consumed and applies the appropriate handlers
in the order listed in the configuration file. The latter is used when
a message is being published and applies the handlers in the
reverse order.