Web Application Development in PLT Scheme - send/suspend
April 13th, 2008
send/suspend
Using continuation-based web servers is a very powerful way to develop web applications. Continuations can essentially remove problems associated with back buttons and multiple tabs/windows in web browsers. The basic idea behind web server continuations is that a continuation reference id is passed in the URL of a web request and then the continuation with that id is executed on the server. This allows web developers the ability to maintain state without using sessions. You could even think of the continuation URLs as a basic session id that’s directly tied to the execution of server-side code.
The continuation ids aren’t true session variables. They simply maintain the state of current execution. If you were to use two browser windows and point them at the same continuation-based website, each browser window would have its own unique execution path. On a normal website using cookies to maintain state, both of your browser windows would share data (such as a user id). Using continuations to maintain state allow both of your browser sessions to be completely independent of eachother. Keep in mind that there’s nothing stopping you from using cookies with continuation-based web servers like PLT Web Server.
If you followed along with my previous posts here and here, you should have scheme installed and ready to go. In your /usr/plt/collects/web-server/default-web-root/servlets/examples/ directory there are a bunch of scheme servlet examples. One of the more famous examples used for introducing people to continuation web programming is the add.ss servlet in the examples directory.
I’m including that add.ss example in this article’s source code so all you have to do to test it out is require “main.ss” in your mzscheme prompt and visit http://localhost:3000/servlets/add.ss. When you’re submitting numbers to the servlet, take notice of the strange ids in the URL that look something like “2*1*34452322″. The first number is the servlet instance id, the second number is the continuation id, and the third number is a randomly-generated id that helps prevent users from guessing continuation URLs.
All the code for this article can be found under svn: http://alwaysmovefast.com/public_svn/article_src/two.
Like the simple.ss example, add.ss has a start function where the initial request is passed. When a request for add.ss comes into the server, the start function is executed. Before the start function returns, it calls (request-number “first”), followed by (request-number “second”), and then adds the result of those two function calls.
When (request-number “first”) is called, it runs the build-request-page function inside of a send/suspend call. send/suspend captures the current continuation and binds it to a URL (k-url) that’s passed to the function returned by (build-request-page which-number). Once the lambda function is called with k-url, it’s expected to generate an HTTP response. That response is the HTML form you see on the page.
When you use send/suspend with the continuation-bound URL (k-url), you can use that in your links and form actions. That’s exactly what build-request-page is doing here:
The responses generated by (start) and (build-request-page) are inside a quasi-quote (`). This means that these functions return code as data that will be evaluated later. When you quasi-quote an expression, you’re able to unquote expressions and variables such as k-url and which-number. By unquoting k-url and which-number, we’re telling the interpreter to evaluate them immediately.
When you enter the first number in the form and post it to the continuation URL, the continuation is invoked and the request is returned from send/suspend. The call to send/suspend is wrapped in a request-bindings call. request-bindings takes an HTTP request and returns the variable bindings. The variable bindings can then be extracted. In this case, the code uses extract-binding/single to request a single variable from the request bindings. The text field of the form is named “number”, so in extract-binding/single, we pass ‘number as the binding to be extracted from the request-bindings call. Once the number value is extracted, we have to convert it from a string to an integer by using string->number. The number is then returned as the last evaluated expression of request-number.
After (request-number “first”) returns with an integer, (request-number “second”) is called and follows the same path as (request-number “first”). When (request-number “second”) returns, both values are added together and then converted from an integer to a string in the start function.
The start function then returns its HTTP response with the sum of the two numbers.
There is a major limitation when using send/suspend: you can’t easily generate multiple links and forms from one continuation URL unless you’re content with using URL query strings. This means that all your requests will essentially go to one function and then you will have to dispatch based on the query string values. It’s definitely not very elegant. One of the solutions to this problem is by using send/suspend/dispatch, which I’ll be covering in my next blog post.
Although send/suspend is somewhat limited, you should still hack around with it and create some cool things to get used to it.
Leave a Reply