Storing application state on the server

While storing all the application state in the web browser has advantages, it can be inconvenient when the state is very large, or when it is being sent in hyperlinks and short URLs are wanted. This tutorial looks at a few other ways of storing state. Customised state storage is easiest using the webapp model and the storeFunction and retrieveFunction functions. The following examples look at the functions passed as storer and retriever to these functions.

Storing state in a cookie

If the state is relatively small, and the goal is clean URLs, then it may be sufficient to store the state in a cookie.

String cookieStorer(HTMLDocument doc, String state) {
    cookie = setCookie("login",state);
    addHTTPHeader(doc,cookie);
    return "login";
}

String cookieRetriever(String key) {
    return incomingValue(key,DataCookie);
}

As good practice for these functions, the cookieStorer function returns the 'key' by which the state may be found, and the cookieRetriever function uses it. Retriever functions must throw an exception if the state does not exist, and incomingValue already handles this.

Then, instead of calling runHandler, instead call result = retrieveFunction(@notLoggedIn,cookieRetriever,"login"); and instead of using localControlURL, call void(storeFunction(cookieStorer@(doc),@loggedIn,user)); and construct hyperlinks normally.

A web application using cookies to store its state, and the source code. Note that this application has a security bug in it (see the comments to the showPage function in the source if you can't find it) which it may be a useful exercise to fix.

Storing state on the server

Where the state is very large, it is probably better to store it on the server rather than sending it to the web browser and back every time. This can be done by modifying the storing and retrieving functions slightly. In this example, the state is stored in a database. The exec function comes from the DB module, which provides a database-independent API for queries.

// database is either a global variable or a
// function that returns a database connection handle

String cookieStorer(HTMLDocument doc, Int uid, String state) {
// just store the username in the state
    key = encode(String(uid));
// encrypt it to stop easy takeover of other accounts
    cookie = setCookie("login",key);
    addHTTPHeader(doc,cookie);

// state is base64-encoded and uid is an integer, so they're safe
// but in general you need to escape Strings before placing them
// into SQL queries to prevent SQL injection attacks
    void(exec(database,"REPLACE INTO Sessions SET session = '"+state+"' WHERE user = '"+uid+"'")); 

    return key;
}

Exception SessionExpired;

String cookieRetriever(String key) {
// if the key isn't a valid key, then this will throw an Exception
// another good reason to encrypt them
    uid = Int(decode(key));
    getstate = exec(database,"SELECT session FROM Sessions WHERE user = '"+uid+"'");
    if(getstate.rows == 0) {
// maybe the session has expired
        throw(SessionExpired);
    } else {
        return string(getstate.table[0][0]);
    }
}

Note that if you want to use a storer function that needs to know in advance what key to use, you will have to use partial application to provide this. When storing in a database, this may be common (although new sessions may have a database-provided ID).

You can see the application above, modified to store its state in a database, and the modified source code. Modifying it to use a MySQL or PostgreSQL database is simply a matter of replacing the connection function at the top and changing the import lines.

kaya@kayalang.org | Last modified 29 November 2011 | Supported by Durham CompSoc | Powered by Kaya