Web server

An optional web server is provided in x/84 using the basic web.py python library. It is possible to build web “endpoints” that may make use of x/84’s database and configuration items, these are called “web modules”. Of the default board, intra-bbs messaging is provided by a web module, for example.

Starting a web server

Of your ~/.x84/default.ini file, set the configuration of the [web] section value enabled = yes (by default, it is no). You will also require a certificate, key, and sometimes a chain certificate file – only HTTPS is supported at this time. This is documented in more detail in the “Configuring a hub” section of the message network page.

For the server to successfully launch, at least one module must be enabled, the simple example modules oneliners, lastcallers may be enabled, for example:

[web]
enabled = yes
addr = 123.123.123.123
port = 8443
key = /etc/ssl/www.1984.ws.key
cert = /etc/ssl/www.1984.ws.crt
chain = /etc/ssl/www.1984.ws.chained.crt
modules = oneliners, lastcallers

If everything is configured properly, you should see something like this at startup:

Mon-01-01 12:00AM INFO       webserve.py:207 https listening on 123.123.123.123:8443/tcp

Lookup path

There are only two lookup paths for the values defined by modules, preferably, the sub-folder, webmodules/ of your scriptpath configuration of section [system] in your ~/.x84/default.ini file. These are imported by their python module name, so file scriptpath/webmodules/oneliners.py is simply oneliners. If the file is not found there, it will then look for it in the package path of x84, which can be found using command:

$ python -c 'import os, x84.webmodules; print(os.path.dirname(x84.webmodules.__file__))'

Serving static files

One of x/84’s internal web modules is called static. If you enable this module, x/84 will serve static file content from the www-static subdirectory of your system’s top-level scriptpath. The top-level refers to the first item in this array. If you wish to set the document root to some other location, use the document_root option in the [web] section of your configuration file.

[web]
; other configuration here
modules = static
document_root = /var/www

The static files are served from /www-static/, so if your server is https://123.123.123.123:8443, and the file is style.css, it would be served as https://123.123.123.123:8443/www-static/style.css.

Writing a web module

While some web modules, such as the message network module, operate outside of userland and are leveraged by the engine for low-level functionality. However, you can write your own modules–and even override the internal modules–by placing your scripts in the webmodules subdirectory of your x/84 system’s script directory and adding them to the modules list in the [web] section of your configuration file.

As examples, two web modules have been included with the “default board” installed alongside x/84: :module:`x84.default.oneliners` and :module:`x84.default.lastcallers`. These are rudimentary examples which both read information from DBProxy objects and format them for display on the web. They serve to demonstrate interacting with the engine layer outside of a terminal session; accepting command options through the use of GET parameters; how Python classes ultimately translate into URL handlers; and exposing URL handlers to the x/84 engine process.

The handler class

First and foremost, we need to build a class which will be handling our HTTP requests. x/84’s web server uses web.py internally, and so we give our class a method function for each HTTP verb we want it to respond to. For the purposes of demonstration, the class below will only be responding to GET requests.

class EchoHandler(object):

    """ Demonstration URL Handler """

    def GET(self, echo=None):
        """ Echo back to the user. """

        if not echo:
            echo = u"I can't hear you!"

        return echo

This class will echo back whatever the user writes in the URL. If the user doesn’t write anything, it will display, “I can’t hear you!”

The REST API

Now, we need to inform the x/84 engine process about the existence of our web module and what URL pattern(s) it should be invoked for. We do this by putting a root-level web_module function in our script that returns a dict object with this information.

def web_module():
    """ Return a dict of our REST API. """

    return {'urls': ('/echo(.*)?', 'echo'), 'funcs': {'echo': EchoHandler}}

The first dict entry, urls, is a list where pairs of URL patterns and keywords are associated with one another. The pattern is that each even-numbered entry (0, 2, 4, 6, ...) is a URL pattern and each following odd-numbered entry (1, 3, 5, 7, ...) is a keyword for which URL handler should be invoked for this URL pattern.

The next dict entry, funcs, is a dict that translates those keywords into the class of the web module. In our example, we are translating the keyword, echo, into the class, EchoHandler.

Enabling the module

Now that we’ve finished with the code, we need to add our new module to the modules option in the [web] section of our configuration file. If we saved our script as echo.py in the webmodules subdirectory of our x/84 system’s script path, we would use the name echo to refer to it in the configuration file:

[web]
; other configuration here
modules = echo

Next, we will have to restart x/84 in order for the module to be loaded.

Testing the module

Now, if we visit https://123.123.123.123:8443/echo/test in our web browser, we will see:

test

And if we visit https://123.123.123.123:8443/echo in our web browser, we will see:

I can't hear you!

Take it further

This is a very simple example. For a bit more advanced functionality, look at the source of the :module:`x84.default.webmodules.oneliners` and :module:`x84.default.webmodules.lastcallers` modules. To take it a step further still, consider looking at the :module:`x84.webmodules.msgserve` module in the x/84 server code.