Miscellaneous

Writing Tools

Most of the internal tools used by circuits.web in circuits.web.tools are simply functions that modify the Request or Response objects in some way or another... We won’t be covering that here... What we will cover is how to build simple tools that do something to the Request or Response along it’s life-cycle.

Here is a simple example of a tool that uses the pytidylib library to tidy up the HTML output before it gets sent back to the requesting client.

Code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/usr/bin/env python
from tidylib import tidy_document

from circuits import Component

class Tidy(Component):

    channel = "http"

    def response(self, response):
        document, errors = tidy_document("".join(response.body))
        response.body = document
Usage:

(Server(8000) + Tidy() + Root()).run()

How it works:

This tool works by intercepting the Response Event on the “response” channel of the “http” target (or Component). For more information about the life cycle of Request and Response events, their channels and where and how they can be intercepted to perform various tasks read the Request/Response Life Cycle section.

Writing Dispatchers

In circuits.web writing a custom “dispatcher” is only a matter of writing a Component that listens for incoming Request events on the “request” channel of the “web” target. The simplest kind of “dispatcher” is one that simply modifies the request.path in some way. To demonstrate this we’ll illustrate and describe how the !VirtualHosts “dispatcher” works.

VirtualHosts code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class VirtualHosts(Component):

    channel = "web"

    def __init__(self, domains):
        super(VirtualHosts, self).__init__()

        self.domains = domains

    @handler("request", filter=True, priority=1)
    def request(self, event, request, response):
        path = request.path.strip("/")

        header = request.headers.get
        domain = header("X-Forwarded-Host", header("Host", ""))
        prefix = self.domains.get(domain, "")

        if prefix:
            path = _urljoin("/%s/" % prefix, path)
            request.path = path

The important thing here to note is the Event Handler listening on the appropriate channel and the request.path being modified appropriately.

You’ll also note that in [source:circuits/web/dispatchers.py] all of the dispatchers have a set priority. These priorities are defined as:

$ grin "priority" circuits/web/dispatchers/
circuits/web/dispatchers/dispatcher.py:
   92 :     @handler("request", filter=True, priority=0.1)
circuits/web/dispatchers/jsonrpc.py:
   38 :     @handler("request", filter=True, priority=0.2)
circuits/web/dispatchers/static.py:
   59 :     @handler("request", filter=True, priority=0.9)
circuits/web/dispatchers/virtualhosts.py:
   49 :     @handler("request", filter=True, priority=1.0)
circuits/web/dispatchers/websockets.py:
   53 :     @handler("request", filter=True, priority=0.2)
circuits/web/dispatchers/xmlrpc.py:
   36 :     @handler("request", filter=True, priority=0.2)

in web applications that use multiple dispatchers these priorities set precedences for each “dispatcher” over another in terms of who’s handling the Request Event before the other.

Note

Some dispatchers are designed to filter the Request Event and prevent it from being processed by other dispatchers in the system.