mozilla

Extending Vaurien

Note

Before reading this section, make sure you read Keep-alive vs Disconnect

You can extend Vaurien by writing new protocols or new behaviors.

Writing Protocols

XXX

Writing Handlers

Creating new handlers is done by implementing a class with a specific signature:

from vaurien.handlers import Handler

class MySuperHandler(object):

    name = 'super'
    options = {}

    def __call__(self, client_sock, backend_sock, to_backend):
        # do something here
        return True  # or False, see after.


Handler.register(MySuperHandler)

Vaurien can use this handler and call it when data comes from the backend (the server being proxied) or from a client (making calls to the server).

You must call Handler.register against your class is order to add it to the list of the available plugins.

Let’s see the different attributes and options we have in this class:

  • name - the name under which your backend is known
  • options - a mapping containing your handler options
  • client_sock - the socket opened with the client
  • backend_sock - the socket opened with the backend server
  • to_backend - a boolean giving the direction of the call. If True it means some data is available in the client socket, that is supposed to go to the backend. If False, it means data is available on the backend socket and should be tramsmitted back to the client.

For the handler options, each option is defined in the options mapping. The key is the option name and the value is a 3-tuple providing:

  • a description
  • a type
  • a default value

every option is optional and need a default value

Everytime a handler is used, it gets two extra attributes:

  • settings - the settings loaded for the handler
  • proxy - the proxy instance

The BaseHandler class

XXX

Full handler example

Here is how the delay handler is specified:

from vaurien.handlers.base import BaseHandler


class Dummy(BaseHandler):
    """Dummy handler.

    Every incoming data is passed to the backend with no alteration,
    and vice-versa.
    """
    name = 'dummy'
    options = {'keep_alive': ("Keep-alive protocol",
                            bool, False),
            'reuse_socket': ("If True, the socket is reused.",
                                bool, False)}

    def __call__(self, client_sock, backend_sock, to_backend):
        data = self._get_data(client_sock, backend_sock, to_backend)
        if data:
            dest = to_backend and backend_sock or client_sock
            source = to_backend and client_sock or backend_sock
            dest.sendall(data)

            # If we are not keeping the connection alive
            # we can suck the answer back and close the socket
            if not self.option('keep_alive'):
                data = ''
                while True:
                    data = dest.recv(1024)

                    if data == '':
                        break
                    source.sendall(data)
                dest.close()
                dest._closed = True
        elif not to_backend:
            # We want to close the socket if the backend sock is empty
            if not self.option('reuse_socket'):
                backend_sock.close()
                backend_sock._closed = True

        return data != ''

Using handlers

Once the handler is ready, you can point it to Vaurien by providing its fully qualified name - e.g. the class name prefixed by the module and package(s) names.

Then you can use it with the –behavior option:

$ vaurien --proxy localhost:8000 --backend google.com:80 \
    --behavior 20:path.to.the.callable \
    --handler-delay-sleep 2

Or by using a configuration file:

[vaurien]
behavior = 20:foobar

[handler:foobar]
callable = path.to.the.callable
foo=bar

And calling Vaurien with –config:

$ vaurien --config config.ini