Implementing your own middleware

A middleware class provides four handler functions, one for processing each of the four kinds of messages transports and applications typical sent 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.

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. 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 in setup_middleware() instead.

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.
handle_inbound(message, endpoint)

Called when an inbound transport user message is published or consumed.

The other methods – handle_outbound(), handle_event(), handle_failure() – all function in the same way. Only the kind of message being processed differs.

Parameters:
  • message (vumi.message.TransportUserMessage) – Inbound message to process.
  • endpoint (string) – The transport_name of the endpoint the message is being received on or send to.
Return type:

vumi.message.TransportUserMessage

Returns:

The processed message.

handle_outbound(message, endpoint)

Called to process an outbound transport user message. See handle_inbound().

handle_event(event, endpoint)

Called to process an event message ( vumi.message.TransportEvent). See handle_inbound().

handle_failure(failure, endpoint)

Called to process a failure message ( vumi.transports.failures.FailureMessage). See handle_inbound().

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

    def setup_middleware(self):
        log_level = self.config.get('log_level', 'info')
        self.message_logger = getattr(log, log_level)
        failure_log_level = self.config.get('failure_log_level', 'error')
        self.failure_logger = getattr(log, failure_log_level)

    def _log(self, direction, logger, msg, endpoint):
        logger("Processed %s message for %s: %s" % (direction, endpoint,
                                                    msg.to_json()))
        return msg

    def handle_inbound(self, message, endpoint):
        return self._log("inbound", self.message_logger, message, endpoint)

    def handle_outbound(self, message, endpoint):
        return self._log("outbound", self.message_logger, message, endpoint)

    def handle_event(self, event, endpoint):
        return self._log("event", self.message_logger, event, endpoint)

    def handle_failure(self, failure, endpoint):
        return self._log("failure", self.failure_logger, failure, endpoint)

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.

Project Versions

Table Of Contents

Previous topic

Builtin middleware

Next topic

Metrics

This Page