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:
- 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
orindex.htm
.To use the file dispatcher you will probably want the
handymake-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)))
- The servlet dispatcher, in
dispatch-servlets.ss
is a bit more difficult to use as you need a function from theprivate
subcollection of the web-server, suggesting the code reorganisation isn’t quite finished. Themake
function takes two arguments, the first being acache-table
, and the second being a function that, like for the file dispatcher, maps URLs to paths. To construct acache-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))
- 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!