« Attack of the Spam Bots | Main | Corporate Blanding »
November 23, 2007
Custom Dispatchers in the PLT Scheme Web Server
We've just released 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
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 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 calledindex.htmlorindex.htm.To use the file dispatcher you will probably want the handy
make-url->pathfunction infilesystem-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.ssis a bit more difficult to use as you need a function from theprivatesubcollection of the web-server, suggesting the code reorganisation isn't quite finished. Themakefunction 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-tableuse 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!
Posted by Noel at November 23, 2007 04:12 PM
Trackback Pings
TrackBack URL for this entry:
http://www.untyped.com/mt/mt-tb.cgi/125
