Fermion - The Scheme Web Server

2009 August 19

Fermion is a fast and secure web server written in Scheme. It is optimized to serve dynamic content generated by Scheme scripts. It is part of the larger Spark-Scheme project. It uses light-weight green threads to handle client requests and can efficiently serve thousands of simultaneous connections. The following code starts an instance of Fermion:

;; file: my-http.ss
(import (net) (http))
(web-server-start (web-server))

Create an HTML file called 'index.html' and place it along with 'my-http.ss'. This will be the default page served by our http server. Start the web server with the command `spark my-http.ss'. You will be able to access 'index.html' with the URL: http://localhost.

The forte of Fermion is not in serving static files, but in generating content dynamically based on user input or some other volatile parameter. Let us write a simple web application that will greet the user based on the time of the day.

;; file: greet.ss

(import (aura))

(define (greet new-uri state)
 (((sgml 
 `(html
   (body
    (b ,(greeting-by-hour)))))
 'text)))

(define (greeting-by-hour)
 (let ((hour (date-hour (seconds->date (current-seconds)))))
    (cond ((<= hour 12) "Good morning!") 
          ((<= hour 17) "Good afternoon!") 
          (else "Good evening"))))
(list greet) 

The procedure 'greet' will be executed by the web server, by passing it two arguments. 'new-uri' will be a dynamically generated URI to which any further requests, like a form POST, should be send. 'state' is a hash table that contains the current state of the session. For instance, it will contain values posted by previous requests. The web application exports a list of

procedures that will be executed by Fermion, in the given order. Thus the programmer can design a web application as a sequence of operations, much like a normal console program. The greet sample also makes use of a library called aura which can be used to write SGML documents in Scheme syntax.

Fermion is a secure web server. It is possible to impose some control over HTTP requests by using configuration options. An example is given below:

(define httpd (web-server
               (list 'port 8080 ;; Web server will listen
                                ;; on port 8080.
                     'session-timeout (* 2 60) ;; 2 minutes
                     'max-header-length (* 112 1024) 
                     'max-body-length (* 512 1024)
                     'max-response-size (* 512 1024))))

Fermion provide hooks where new procedures can be plugged-in to implement advanced security and compliance features. For instance, the following sample shows how to hook in a procedure to check the length of the request URI. If the URI contain more than 512 characters, the request will be blocked.

(import (net)
        (http-request-parser)
        (http))
(define httpd (web-server))
;; The security hook procedure.
(define (check-uri-length httpd
                          client-connection
                          http-request)
  (cond ((> (string-length (http-request-uri http-request)) 
     512)
         (printf "Request URI too long. Closing connection.~n")
         (flush-output)
         (socket-close (connection-socket client-connection))
         #f)
        (else ;; Let the web server's request 
              ;; handler do its job.
         #t)))
(web-server-hook! httpd 'before-handle-request
 check-uri-length)
(web-server-start httpd)

User defined policies can be applied on the response by using the 'before-send-response hook.

I hope that Scheme hackers will find Fermion useful in their real-world web development tasks.