23 Nov 2007

by Noel

Custom Dispatchers in the PLT Scheme Web Server

We’ve just released <a
href=”http://planet.plt-scheme.org/display.ss?package=instaweb.plt&owner=schematics”>Instaweb
2.0. Instaweb is our utility that takes care of setting
up the PLT web-server and running servlets. If you have a
servlet in a file called servlet.ss with
Instaweb you just need to write the following lines to get
it running:

(require (planet "instaweb.ss" ("schematics" "instaweb.plt" 2)))
(instaweb)

The new version of Instaweb includes many new options and
works in a slightly different way to the 1.0 branch. To my
mind the best new feature is that Instaweb now configures
the web-server to pass to the servlet all requests
that don’t match a file in the htdocs
directory. This means your servlet no longer has to live
under a URL starting with /servlets. You can
<a
href=”http://planet.plt-scheme.org/package-source/schematics/instaweb.plt/2/1/doc.txt”>read
the documentation to get the full details of what’s new.
What I want to talk about here is how we implelemented this,
as it illustrates some very nice features of the web-server
that aren’t well known.

In the web-server’s terminology a dispatcher is a
function that may generate a response given a request.
Examples includes the filesystem dispatcher, which responds
to requests with the contents of a file, and the servlet
dispatcher, which invokes a servlet. Dispatchers are
arranged in a list. The first dispatcher in the list
inspects the request and, if it decides the request is
relevant, generates a response. Otherwise control is passed
to the next dispatcher in the list. For some time now the
web-server has had a configurable dispatcher pipeline, which
can be set by simply passing a value with the
#:dispatch keyword to the serve
function.

The web-server provides a number of dispatchers, all in
the <a
href="http://svn.plt-scheme.org/plt/trunk/collects/web-server/dispatchers/">dispatchers

subdirectory of the web-server collection.
They all provide a make function that does most
of the work. Here’s how to use the file, servlet, and
sequence dispatchers, the most generally useful ones:

  1. The file dispatcher, in
    dispatch-files.ss, takes a single parameter, a
    function that converts a URL to a path (and another value
    that the dispatcher ignores). The path can name a file,
    which the dispatcher will serve if such a file actually
    exists, or it can name a directory, in which case the
    dispatcher will look for a file within that directory called
    index.html or index.htm.

    To use the file dispatcher you will probably want the
    handy make-url->path function in
    filesystem-map.ss. Pass this function a base
    path (the directory where your files live), and it will
    return a function suitable to pass to the file
    dispatcher.

    Here’s an example of use:

    (require
    (prefix file: (lib "dispatch-files.ss" "web-server" "dispatchers"))
    (lib "filesystem-map.ss" "web-server" "dispatchers"))
    
    (define base-path (string->path "/my/directory/of/files"))
    
    ;; htdocs-url->path : path -> (url -> path (list-of path-element))
    (define (htdocs-url->path path)
    (make-url->path (path->complete-path path)))
    
    ;; dispatch-htdocs : (connection request -> response)
    (define dispatch-htdocs
    (file:make #:url->path (htdocs-url->path base-path)))
  2. The servlet dispatcher, in dispatch-servlets.ss is a bit more difficult to use as you need a function from the privatesubcollection of the web-server, suggesting the code reorganisation isn’t quite finished. The make function takes two arguments, the first being a cache-table, and the second being a function that, like for the file dispatcher, maps URLs to paths. To construct a cache-table use the following lines of code:
    (require
    (lib "cache-table.ss" "web-server" "private"))
    
    (define cache-table (box (make-cache-table)))

    If you want all URLs to go a particular servlet, as in Instaweb, the URL to path function just needs to return the path of the servlet. The function used in Instaweb is this:

    ;; serlvet-url->path : url -> path (list-of path-element)
    (define (servlet-url->path url)
    (let ([complete-servlet-path (path->complete-path servlet-path)])
    (values complete-servlet-path (explode-path* complete-servlet-path))))

    Now we can create a dispatcher as follows:

    ;; clear-servlet-cache! : -> void
    ;; dispatch-servlets:    connection request -> response
    (define-values (clear-servlet-cache! dispatch-servlets)
    (servlet:make (box (make-cache-table))
    #:url->path servlet-url->path))
  3. The sequencer dispatcher couldn’t be easier to use. It just takes any numbe of dispatchers and creates new dispatcher that tries them in sequence. For example:
    ;; dispatch-all : connection request -> response
    (define dispatch-all
    (sequencer:make dispatch-htdocs
    dispatch-servlets))

With the above you should be able to create your own custom dispatchers. If you have problems just read the (very short) Instaweb code!

Posted in Web development | Comments Off on Custom Dispatchers in the PLT Scheme Web Server

Comments are closed.