Overview

Introduction

Serf is a simple web server. It’s a development tool to be used during the development of rich internet applications: it’s not intended for deployment.

A rich internet application is a web application in which most of the user interface is handled by Javascript in the browser, in a single-web page that provides interactivity. Typically this is done by building on existing Javascript libraries or frameworks. The application interacts with the server using Ajax. Ideally the server implements a restful web service so that client and server are decoupled.

Serf offers a number of features:

  • serve up static files, both Javascript and CSS, but also files that can be used during testing such as *.json files.
  • URLs to these static files are unique as they contain a hash. this means that whenever you change the contents of a static file, the URL to that file changes automatically. This makes it possible to easily defeat browser caches during development, while still allowing infinite caching.
  • serf supports hurry.resource, a system for distributing Javascript libraries as Python libraries. This makes it easy to create Javascript code that relies on other javascript code.

How to run Serf

Serf installs itself as a command-line executable called serf. It takes as an argument the dotted name of a Python package. This package should contain serf configuration. With the -p option the port number can also be indicated; it defaults to 8080.

To serve up the Python package foo.bar, write:

$ bin/serf foo.bar

To serve up the Python package qux under port 7000, write:

$ bin/serf -p 7000 qux

Configuring Serf

The primary way to configure Serf is to use hurry.resource. To publish any static resources (javascript, CSS, images, HTML, etc) we need to declare a library:

import serf

mylibrary = serf.Library('mylibrary', 'resources')

In this example, mylibrary` is the name of the library, and resources is a path to a directory that contains the static resources involved. Note that these paths are relative from the same package that the module itself is defined in.

When pointed to a package that contains this code, Serf will start to serve up the static resource /mylibrary. Any actual resources however will be in hashed URLs, so that URLs to resources will look like this:

/mylibrary/618ab5135353a/foo.js

Note that you have to use serf.Library for Serf to pick up the library automatically. Serf can also pick up resource.Library instances, but only when they’re exposed using the special hurry.resource.libraries entry point as described in the hurry.resource documentation. When preparing a library for general distribution you should get rid of the dependency on serf.Library and start using the entry point instead.

Special defaulting rules

A single-page rich client application is driven by a single HTML page, and typically the page we pick for this we name index.html. When you go to /mylibrary by itself Serf will redirect you to /mylibrary/<hash>/index.html automatically.

To make the root URL work (/), create a library named index, with a file index.html. The root URL will automatically redirect to this file.

Creating a resource inclusion

We can now use hurry.resource to create a resource inclusion that we want to be included on a web page. Let’s define a simple one for foo.js in mylibrary:

from hurry import resource

foo = resource.ResourceInclusion(mylibrary, 'foo.js')

This defines that in the library mylibrary there is a file called foo.js that can now be automatically included. Resource inclusions can be more involved and mark dependencies themselves. For more information about that, see the hurry.resource documentation.

Making a HTML page include a resource inclusion

Now we need to express that a particular HTML page actually depends on this resource inclusion to work:

import serf

dep = serf.Dependency(mylibrary, 'index.html', [foo])

Serf will now automatically include a reference to foo.js in the index.html web page in the mylibrary resource. So, when index.html is requested, the right resource (and all its dependencies) should be present.

Pre-wrapped resource inclusions

A number of Javascript libraries have been pre-packaged with hurry.resource, so to use them all you have to do is make your package depend on them in setup.py and then you can import the resource inclusions from them and depend on them. Here is an incomplete listing:

* `hurry.jquery <http://pypi.python.org/pypi/hurry.jquery>`_

Recognizing resource extensions

By default Serf recognizes a number of file extensions (.html, .js, .css, .json`, .png, .jpg, .gif) and will serve them with the appropriate content type. Other extensions are served as text/plain by default. If you want to configure what content type should be served for a file extension, you can use Extension:

foo = serf.Extension('foo', 'text/foo')

Now all files with the extension .foo will be served with the text/foo content type.