Tuesday, 14 November 2017

Cherry Blossoms with CherryPy

CherryPy is a python web framework which I really like. It is minimalistic in nature, is very pleasant to work with, and still we can accomplish much. Below I will detail a basic web app setup with CherryPy demonstrating basic usage which one can expand further.
# blossoms.py
"""A blossoms server with CherryPy."""

import cherrypy
import os

class CherryBlossom(object):
    """The server route handler methods."""

    def __init__(self):
        pass

    @cherrypy.expose
    def index(self):
        """The root url, when not mapped to static response, this action will be invoked."""
        return self.about()

    @cherrypy.expose
    def about(self):
        return 'Enjoy cherry blossoms.'

    @cherrypy.expose(['greet'])
    @cherrypy.tools.json_out()
    def get_greeting_message(self, name):
        """Return greetings to the given name in JSON format."""
        return {'msg': 'Greetings %s' % name'}

    @cherrypy.tools.register('before_finalize', priority=60)
    def secureheaders():
        """Update server response headers."""
        headers = cherrypy.response.headers
        headers['X-Frame-Options'] = 'DENY'
        headers['X-XSS-Protection'] = '1; mode=block'
        headers['X-Powered-By'] = 'Cherry Blossoms'
        headers['Server'] = 'Sakura'

class Blossoms(object):
    """Server configuration and initialisation."""

    def __init__(self):
        pass

    def start(self):
        """Start the service."""
        blossom = CherryBlossom()
        # routes
        dispatcher = cherrypy.dispatch.RoutesDispatcher()
        dispatcher.explicit = False
        dispatcher.connect('home', '/', blossom.index)
        dispatcher.connect('about', '/about/', controller=blossom, action='about', conditions=dict(method=['GET']))
        dispatcher.connect('greetings', '/greet/{name}/', controller=blossom, action='greet', conditions=dict(method=['GET']))
        # config
        root_dir = os.path.dirname(os.path.abspath(__file__))
        global_conf = root_dir + '/blossoms.conf'
        cherrypy.config.update(global_conf)
        conf = {
            '/': {
                'request.dispatch' : dispatcher,
                'tools.staticdir.dir': root_dir
            }
        }
        app = cherrypy.tree.mount(None, '/', global_conf)
        app.merge(conf)
        if hasattr(cherrypy.engine, "signal_handler"):
            cherrypy.engine.signal_handler.subscribe()
        if hasattr(cherrypy.engine, "console_control_handler"):
            cherrypy.engine.console_control_handler.subscribe()
        cherrypy.engine.start()
        cherrypy.engine.block()
# pyserver.py
from blossoms import Blossoms

blossom = Blossoms()
blossom.start()
The configuration are present in the blossoms.conf file.
[global]
server.socket_host: '127.0.0.1'
server.socket_port: 1729
server.thread_pool: 8

[/]
tools.gzip.on: True
tools.staticdir.on: True
tools.staticdir.index: 'index.html'
tools.staticdir.debug: True,
tools.secureheaders.on = True,
tools.response_headers.on: True,
tools.response.stream: True,
tools.sessions.on = True
tools.sessions.secure = True
tools.sessions.httponly = True
log.screen: True
Calling the /, FQDN alone is configured to serve static content. The GET endpoints are /greet/{name} and /about/. The RoutesDispatcher uses routes package to do the dispatch. The requirements.txt is below.
cherrypy==11.1.0
routes==2.4.1
We load the config and merge it with few updated setting from the code before starting the server. We have a server that handles static content, does REST based routing, custom response headers, JSON, html responses, with configurable settings. Enjoy cherry blossoms.

No comments:

Post a Comment