Subsections
The main difference between a web application and a regular application is that in a web application, each page requested by
a client is independant from the other pages requested by the same client. In other words, we have to start from
scratch for each page.
Of course, for any serious application, this is not acceptable and we need to keep some data about a given user across
several pages.
Let's assume for example that we want to keep the first name and the last name of the user across several pages. We ask the
user for his first name and last name on the first page of the website (using a form) and we need to use that data in other
pages of the site.
First of all, we have basically two options:
- keep the first name and the last name from one page to the next on the client side
- store the first name and the last name somewhere on the server side, and only keep
a pointer to that data on the client side: this is what sessions do
If we choose the first option, we have several ways to do this:
- Keep the first name and the first name in the URL: each time there is a link to another page, we could add the
following arguments to the URL: "firstName=....&lastName=..."
- Keep the data in hidden fields of a form: in each page, we could have a form with two hidden fields like this:
<form method="post" name="myForm" action="dummy">
<hidden name="firstName" value="...">
<hidden name="lastName" value="...">
</form>
Everytime there is a link to another page, we have to submit the form to get the data. We can use something like this:
<a href="#"
onClick="
document.myForm.action='...put link here...';
document.myForm.submit();
return false;">...</a>
- Store the first name and the last name in a cookie
The first two options are not really handy as they force you to use extra code for each link. The third option is better but
it is not very handy if we have lots of data to keep about a user.
So another option is to store the data on the server side and to just keep a pointer to that data on the client side. This
is what sessions do ...
The pointer to the data is usually called a sessionId. Since we need to keep the sessionId from one page
to the next on the client side, we still have the same options as described in the previous section:
The session data itself (in our example: the first name and the last name) is stored on the server side.
Again, there are many options to store it:
- Store it in RAM
- Advantage: very fast; can store any python object
- Disavantage: doesn't work in in a multi-process environment; we lose the data when the server is stopped
- Store it in the filesystem
- Advantage: never lose data; works in a multi-process environment
- Disavantage: slow; lots of reads/writes to disk; can only store pickable python objects
- Store it in a cookie in the client's browser
- Advantage: works in multi-thread or multi-process environments. Server is not vulnerable to attacks that consist in artificially starting thousands of sessions on the server to bring it to its knees.
- Disavantage: can only store pickable python objects. Session data must be quite small (some browsers limit the size of cookie data to 4KB)
- Store it in a database
- Advantage: never lose data; works in a multi-process environment
- Disavantage: slow er then RAM; lots of reads/writes to database; can only store pickable python objects
- Store it anywhere you can think of ...
Some systems can also mix some of these options: for instance, session data can be stored in RAM and saved to disk or
to a database once in a while.
Another option is to store everything in RAM and save it to disk when the server shutsdown (although this might be dangerous
because we might not have time to save it if the server crashes or is killed badly).
Also, session data might not change too often so another option is to store it in RAM and save it to disk or to a
database only when it changes.
The following options are supported in CherryPy:
- sessionId: for now, the sessionId is always stored in a cookie
- session data: two options are build in and you can implement very easily any storage method you want ...
- Store everything in RAM; never save it to disk or to a database
- Store everything in the filesystem; read and save the data for each request
- Store the session data in the client browser, as a cookie
- Write your own storage functions so you can store the data wherever you want
You should use the first option (RAM) if you don't use multiple processes (using multiple threads is fine) and if you don't care about losing session data when the server is stopped/restarted.
You should use the second option (filesystem) if you don't have a database and don't want to lose data.
The third option is quite interesting but only works if the session data for each user is quite small (some browsers limit the size of cookies to 4KB).
The fourth option (storing your session data in a database) is the recommended option for most "real-world" websites.
In order to use sessions, you must first enable sessions by setting a few configuration variables on the config file, under
the [session] scope.
The new configuration options are:
- storageType: Can be either "ram", "file", "cookie" or "custom": this tells whether you want to store session data in RAM, to disk, in a cookie, or write your own storage functions.
- storageFileDir: This must be set if you set storageType to "file". Set it to the directory where the session
data will be stored
- timeout: Number of minutes after which a session expires if there was no activity. The default is 60 minutes.
- cookieName: Name of the cookie that stores the sessionId: The default is "CherryPySession"
Once you have enabled sessions in the config file, the way it works is very easy: CherryPy just makes available for you
a global variable called sessionMap, which is in fact a dictionary where you can store your data and retrieve it
in another page.
For instance, if you want to store session data in RAM and if you want sessions to expire after 2 hours, use the
following config file (RootServer.cfg):
[session]
storageType=ram
timeout=120
If you want to store session data to disk (in a directory called /home/user/sessionData), use the following
config file:
[session]
storageType=file
storageFileDir=/home/user/sessionData
The following example is a trivial page view counter:
CherryClass Root:
mask:
def index(self):
<py-code="
count=sessionMap.get('pageViews', 0)+1
sessionMap['pageViews']=count
">
<html><body>
Hello, you've been here <py-eval="count"> time(s)
</body></html>
From CherryPy-0.9-gamma and later, you can now write your own custom functions to save/load session data. This allows you to store them wherever you want.
In order to do so, all you have to do is declare the two following special functions somewhere in your code:
- saveSessionData(sessionId, sessionData): Store the sessionData for this sessionId somewhere
- loadSessionData(sessionId): Retrieve the sessionData for this sessionId and returns it
The following example shows how to store the session data in a MySql database. It assumes that there is a table called "session_data":
mysql> describe session_data;
+-------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+---------+-------+
| sessionId | varchar(50) | YES | | NULL | |
| sessionData | text | YES | | NULL | |
+-------------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
Here is an example code that uses the session_data MySql table to store session data:
use MySql
import pickle, base64, StringIO
CherryClass MyDb(MySql):
function:
def __init__(self):
self.openConnection('localhost', 'test', '', 'test')
def saveSessionData(sessionId, sessionData):
# Pickle sessionData and base64 encode it
f = StringIO.StringIO()
pickle.dump(sessionData, f, 1)
dumpStr = f.getvalue()
f.close()
dumpStr = base64.encodestring(dumpStr)
myDb.query("delete from session_data where sessionId='%s'" % sessionId)
myDb.query("insert into session_data values('%s', '%s')" % (
sessionId, dumpStr))
def loadSessionData(sessionId):
dumpStrList = myDb.query("select sessionData from session_data where sessionId='%s'" % sessionId)
if not dumpStrList:
return None
dumpStr = base64.decodestring(dumpStrList[0][0])
f = StringIO.StringIO(dumpStr)
sessionData = pickle.load(f)
f.close()
return sessionData
CherryClass Root:
view:
def index(self):
i = sessionMap.get('counter', 0) + 1
sessionMap['counter'] = i
return str(i)
See About this document... for information on suggesting changes.