Bill Clementson writes about my recents posts on the continuation based web server framework in Sisc Scheme I'm writing.
First I should note that I'm writing this to learn more about the implementation side of this sort of server so much of it is the result of lots of tinkering. If I really wanted to just use such a framework I'd probably use Seaside which is a great system. Doing this I've learnt a lot more about continuations in Scheme. It's been a very worthwhile exercise.
To get things up quickly I used a Java based web server (Jetty). I wrote a handler in Scheme to do the continuation side of things. Jetty is very easy to use from Sisc and fit my goal of wanting to run the server from the REPL and be able to modify the handlers from the REPL in much the same manner as I use AllegroServe under Common Lisp. The server itself runs from a Sisc thread.
To answer Bills query, the non serializable stuff I had to prevent from being wrapped up in the continuation was the request and response objects. These hold references to sockets and streams. In Sisc global state is not held within the continuation object so I stored these objects inside a Scheme parameter object. These are basically thread local variables. Once assigned to these I reset the references to the request and response objects to #f. Code is something like:
(define current-request (make-parameter #f))
(define current-response (make-parameter #f))
(define (http-handler request response path-in-context path-params)
(current-request request)
(current-response response)
(set! request #f)
(set! response #f)
...)
To serialize the continuation I use:
; k is the continuation object
(call-with-serial-output-file filename
(lambda (port) (serialize k port)))
And to get it back:
(call-with-serial-input-file filename deserialize)
Here's the code to something similar to seasides WACounter example:
(define-page (make-counter-page)
(let ((counter 0))
(lambda (kurl)
`(html (head (title "Counter"))
(body (p ,(number->string counter))
(p ,(block-href (lambda () (inc! counter)) "++")
" "
,(block-href (lambda () (dec! counter)) "--")))))))
(define (counter-app)
(show (make-counter-page))
(counter-app))
(register-application counter-app)
Once you run the 'register-application' function you get the counter-app function assigned to an url. Accessing that url from the web browser will case the page produced by 'make-counter-page' to be shown. The calls to 'block-href' create 'a href' links to an url which when pressed runs the code in the lambda function and then returns back to the original page. These calls decrement or increment the counter and it gets redisplayed when the original page is shown.
The 'kurl' passed to the page is an url that can be used in the page that when accesse will return from the original show call where you can access form variables, etc. This example doesn't use that functionality. Note that you can use the back button and the state is successfully 'restored' to where it was when you were originally at that page. You can 'clone' the window by opening a new browser and entering the url you are currently at. This effectively makes a copy of the current state with two independant 'counter' variables.
10:17:12 PM
|