Developing on Commissaire HTTP

Commissaire HTTP provides a multithreaded REST interface into Commissaire functionality. The server is broken up into 5 main parts: Router, Dispatcher, Function Handler, Class Handler, and the CommissaireHttpServer itself.

Router

The Router maps URI paths to controllers. The following example would route the path /hello/ to the controller at commissaire_http.handlers.hello_world if the HTTP method is GET.

mapper = Router()
mapper.connect(
    '/hello/',
    controller='commissaire_http.handlers.hello_world',
    conditions={'method': 'GET'})

Dispatcher

The Dispatcher uses a Router and, as it’s name suggests, dispatches the requests to the proper controller. It also takes care of loading handlers. The following example creates a new Dispatcher instance using a previously created Mapper. It will load handlers found in the commissaire_http.handlers and mypackage.handlers packages.

dispatcher = Dispatcher(
    mapper,
    handler_packages=[
     'commissaire_http.handlers',
     'mypackage.handlers'])

Handlers

Handlers, also called controllers, do the majority of the business logic. A Handler can be a function or a class, but must follow a specific convention so the Dispatcher knows it’s valid during loading.

Function Handler

Function Handlers must take two parameters: message and bus. The first input, message, is the jsonrpc message. The second input, bus will either be a valid connection to the bus or, if the bus is not enabled, None.

When referencing a Function Handler as a controller use the full package path to the function. If the function is hello_world and it lives under commissaire_http.handlers then the controller would be commissaire_http.handlers.hello_world.

The following example would show the user {"Hello": "there"} or {"Hello", "{{ name }}"} depending on parameters. Remember, the return of the handler must be a valid jsonrpc message as well!

Note

The method in the incoming jsonrpc message is hijacked and filled with the HTTP method that was used to call the handler.

def hello_world(message, bus):
    """
    Example function handler that simply says hello. If name is give
    in the query string it uses it.

    :param message: jsonrpc message structure.
    :type message: dict
    :returns: A jsonrpc structure.
    :rtype: dict
    """
    response_msg = {'Hello': 'there'}
    if message['params'].get('name'):
        response_msg['Hello'] = message['params']['name']
    return {
        'jsonrpc': '2.0',
        'id': message['id'],
        'result': response_msg,
    }

Class Handler

A Class Handler is not much different than a Function Handler. Instead of defining a single function, a class is declared with methods that take three parameters: self, message, and bus. If the method should not be considered a handler it must start with an underscore.

One major difference between a Class Handler and Function Handler is that Class Handlers are instantiated when they are loaded!

When referencing a Class Handler as a controller, use the full package path to the class and the method. If the class is ClassHandlerExample, the method is hello, and it lives under commissaire_http.handlers then the controller would be commissaire_http.handlers.ClassHandlerExample.hello.

The following example exposes hello in the same way as the Function Handler example above. It then uses hello_world to do the heavy lifting.

class ClassHandlerExample:
    """
    Example class based handlers.
    """

    def hello(self, message, bus):
        """
        Example method handler that simply says hello. If name is given
        in the query string it uses it.

        :param message: jsonrpc message structure.
        :type message: dict
        :returns: A jsonrpc structure.
        :rtype: dict
        """
        return hello_world(message, bus)

    def _ignored(self):
        """
        This method would not be loaded as a handler but could be used by
        handlers in this class.
        """
        return 'I am ignored.'

CommissaireHttpServer

In the following example, a CommissaireHttpServer is created which binds to address 127.0.0.1 and port 8000 and uses a previously created Dispatcher. It then is set to run (block) until killed.

server = CommissaireHttpServer('127.0.0.1', 8000, dispatcher)
server.serve_forever()

Code Example

See http_server.