dave-shawley/glinda

View on GitHub
examples/contentneg.py

Summary

Maintainability
A
25 mins
Test Coverage
#!/usr/bin/env python
"""

Example tornado application that handles content negotiation.

This is an example of using glinda's content negotiation functionality
in a simple Tornado application.

"""
import importlib
import json
import logging
import os

from glinda import content, httpcompat
from ietfparse import headers
from tornado import httpserver, ioloop, web


# The following will be supported if available
def maybe_import(name):
    try:
        return importlib.import_module(name)
    except ImportError:
        return None

msgpack = maybe_import('msgpack')
yaml = maybe_import('yaml')


class HttpbinHandler(content.HandlerMixin, web.RequestHandler):
    """Mimics http://httpbin.org/{get,post}"""

    def get(self):
        self.send_response(self.standard_response_dict)
        self.finish()

    def post(self):
        response = self.standard_response_dict
        response['data'] = repr(self.request.body)
        response['files'] = {}
        response['form'] = {}
        response['body'] = self.get_request_body()
        self.send_response(response)
        self.finish()

    @property
    def standard_response_dict(self):
        return {
            'args': httpcompat.parse_qs(self.request.query),
            'headers': dict(self.request.headers),
            'origin': self.request.remote_ip,
            'url': self.request.uri,
        }


class RFC2295Handler(content.HandlerMixin, web.RequestHandler):
    """Implements a bare-bones version of RFC2295 Negotiation."""

    def prepare(self):
        super(RFC2295Handler, self).prepare()
        if not self._finished:
            negotiate = headers.parse_list_header(
                self.request.headers.get('Negotiate', ''))
            if 'vlist' in negotiate:
                variants = []
                for content_type in self.registered_content_types:
                    variants.append('{{"{0}" 1.0 {{type {1}}}}}'.format(
                        self.request.uri, content_type))
                self.set_header('Alternatives', ', '.join(variants))
                self.set_header('TCN', 'list')

    def send_response(self, response_dict):
        try:
            super(RFC2295Handler, self).send_response(response_dict)
        except web.HTTPError as error:
            if error.status_code == 406:
                self.set_status(300, 'Multiple Choices')
                self.set_header('Vary', 'negotiate, accept')
                return
            raise

    def get(self):
        self.send_response({'hi': 'there'})


if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG,
                        format='%(levelname)1.1s %(name)s: %(message)s')
    content.register_text_type('application/json', 'utf-8',
                               dumper=json.dumps, loader=json.loads)
    if msgpack:
        content.register_binary_type('application/x-msgpack',
                                     dumper=msgpack.packb,
                                     loader=msgpack.unpackb)
    if yaml:
        content.register_text_type('application/yaml', 'utf-8',
                                   dumper=yaml.dump, loader=yaml.load)

    app = web.Application([
        web.url(r'/', HttpbinHandler),
        web.url(r'/negotiate', RFC2295Handler),
    ], debug=True)
    server = httpserver.HTTPServer(app)
    server.listen(int(os.environ.get('PORT', '8000')))
    iol = ioloop.IOLoop.instance()
    try:
        ioloop.IOLoop.instance().start()
        iol.start()
    except KeyboardInterrupt:
        logging.info('stopping application')
        iol.add_callback(iol.stop)