Modal Web Server Example Part 1

In part two I describe some additional changes to the server code. Part Three makes no changes to the server code, but discusses some advantages of continuation based frameworks. Part Four extends the framework, adding a callback system, and fixing an important bug. The modal-web-server-0.1.tar.gz file contains the version used in this document.

Overview

There has been quite a bit of discussion lately in various places about continuation based web servers and continuation based web frameworks. Using the Scheme programming language it can be quite easy to put together a very simple continuation based web server. This document shows one approach to get a working server using Chicken Scheme that has enough features to allow playing around with some of the possiblities of this kind of web server design.

I've called the server a 'Modal' web server. 'Modal Web Server' or 'Modal Web Framework' is the term that Avi Bryant, the author of Seaside, has coined to describe continuation based web server designs.

I chose Chicken Scheme to base the example on because it has a working HTTP library that is easy to use. It is also a very portable Scheme implementation implemented in standard C.

This example is not suitable for production use, although it could be extended for this purpose, and at the end of this document I will explain some of the limitations. The modal-web-server-0.1.tar.gz file contains the version described by this document.

What is a Modal Web Server?

A modal web server can be built upon an existing web server framework fairly easily if the language you are using supports continuations. That's why I'm using Scheme in this example.

The advantage of this type of server design is that the control flow for a web application does not need to be split up into a page request/response model, or state machine. Instead you can write the control flow in a procedural manner and the framework manages the problem of maintaining state, allowing the use of the back button and forking of browser windows and keeping everything consistent.

Example

The start to this type of server is being able to register a function that displays HTML with a URL. When that URL is called the function is called and the HTML it generates is displayed to the user.

This is fairly easy to do in a language with first class functions. Using Chicken Scheme with modal-web-server.scm (from modal-web-server-0.1.tar.gz) loaded you get exactly this.

Start a server with:

(spawn-server 5000)

This starts an HTTP server running on port 5000. To make it easy to generate HTML I use the Scheme SSAX libraries. This allows generating HTML and XML using a format called SXML. A 'hello world' function is as simple as:

(define (page1 url) (sxml->html-string '(html (head (title "Hello World!")) (body (p "Hello World!")))))

'page1' is a function that when called returns the HTML for a page that displays the text 'Hello World!'. To register this with the server and assign it a URL so it can be accessed from the browser use:

The result of this call is a URL that is associated with the function. Accessing this URL from the browser will run the function and display the 'Hello World!' page. In this example we don't use the 'url' argument passed to 'page1'. This argument is an URL which can be placed inside an anchor on the page which when requested, continues computation of the function that originally called 'show'.

An example is easier than an explanation. 'show-message' is a function that displays an HTML page with a message and an 'Ok' anchor. The 'Ok' anchor goes to an URL as described above:

(define (show-message msg) (show (lambda (url) (sxml->html-string `(html (head (title ,msg)) (body (p ,msg) (p (a (@ (href ,url)) "Ok")))))))

We can use this by registering with the server a function that calls it a couple of times:

(register-function (lambda () (show-message "Hello") (show-message "World")))

Accessing the registered function displays the first 'Hello' page. Clicking 'Ok' will then resume the function and display the second 'World' page. Clicking 'Ok on that page exits the function.

Between calls to 'show' or 'show-message' we can create state, modify it, and it will be saved between page requests. The common example for modal web applications is the 'Counter' example. This example displays a single page with a number on it. Clicking '++' increases the number. Clicking '--' decreases the number.

To do this example in our framework I use a helper function 'function-href'. This function generates an anchor that when pressed calls a given function:

;; Take the result returned from a page function and send the data to ;; the web browser. If the page function returns #f then take no ;; action. (define (process-page-function-result result) (when result (http:write-response-header) (print "Content-type: text/html\r

Content-length: " (string-length result) "\r

\r

" result)))

'register-function', which we've been using to assign an URL to functions does a similar job to 'show', except it doesn't expect the function to generate HTML. It only exists to run the function:

(define (register-function function) (let ((kid (get-unique-continuation-id))) (hash-table-set! kid-registry kid function) (http:add-resource (string-append "/" (symbol->string kid)) (lambda (r a) (call/cc (lambda (exit) (suicide exit) (let ((k (hash-table-ref kid-registry kid))) (k) (process-page-function-result "") ((suicide) #f)))))) (symbol->string kid)))

Summary

As you can see, in a language which supports continuations natively it is quite easy to tack on a modal web framework to an existing HTTP framework.

I'll continue using this framework to show examples of what can be done with a modal web framework and different implementation options. I welcome an questions. If you wish to email me or continue reading about updates to this framework, see my weblog.

Other, more full featured, frameworks are in existance:

'Seaside', written in Smalltalk.

Sisc Scheme ships with an example that runs as a Java Servlet.

PLT Scheme has a continuation based server with its distribution.

Cocoon is a Java framework that uses a modified Javascript interpreter that supports continuations.

Of these, Seaside is probably the most full featured web application framework that uses the continuation model.

Core Technology, the company I work for, has a commercial continuation based framework that supports clustering, load balancing and persistent sessions, amongst other features. It serves as the 'engine' for a number of products built on top of it to make building enterprise level web applications easier. So there is definitely commercial interest in these ideas.

Copyright (c) 2004, Chris Double. All Rights Reserved.