Session Management with Mason

This Perl-based web helper and MySQL work together to let you quickly build a user registration system for your web site.
Apache::Storable and Mason

Each time a user's browser sends an HTTP request to the web server, it sends whatever cookies have been stored by that domain. So if a cookie was set by, my browser will return only that cookie—which is, after all, simply a name-value pair—when I visit again.

The cookie version of Apache::Storable takes advantage of this by storing a unique identifier in a cookie. This unique identifier corresponds to the id column in the sessions table. This allows us to retrieve any data that have been stored in a_session. Because a_session is defined to be infinitely long, the amount of data we can store is limited only by our database and our file system.

Data stored in table sessions by Apache::Session is available to programs via the global %session hash. %session is created anew for each incoming HTTP request, and refers to only the data stored in a_session. Storing something in %session places it in the a_session column, and retrieving something from %session gets the value from a_session. Assuming that the variables $first_name, $last_name and $email contain the appropriate pieces of information, we could store them reliably with the following lines of Perl:

$session{first_name} = $first_name;
$session{last_name} = $last_name;
$session{email} = $email;

Since each user (actually, each session) is stored in a separate row of the database, we do not need to worry about users clashing with each other.

In order for sessions to work, we must make a connection between the Apache::Session::DBI module and the corresponding sessions table on disk. This connection must take into account three different possibilities: (a) that the user sends us a valid ID cookie, (b) that the user sends us an invalid ID cookie, and (c) that the user sends us no ID cookie at all.

The first case is the easiest; the program merely needs to re-establish the connection between %session and the appropriate row in sessions, using Perl's “tie” mechanism. In the second case, the program must create a new session if it could not re-establish a previous one. And if the user sends no cookie at all, then we must create a new row in sessions, attach a unique ID to it and send that unique ID to the user's browser in the form of a cookie.

When working with Mason, we put all this in our start-up file. This file, which the Mason documentation calls (but which I prefer to call, defines all of Mason's main behaviors and allows us to define global variables that other elements of the system will require. Defining %session in also ensures that it is available in all Mason components. See Listing 1 for a simple example of for a site that wants to include sessions. (Much of Listing 1 comes straight from the Mason documentation.)

Listing 1

The most important part of this file is a call to Perl's eval command. eval comes in two forms, one of which takes a code block as an argument, and forms as a primitive form of error-checking. Inside our code block, we attempt to use Perl's tie command to connect the hash %HTML::Mason::Commands::session to the Apache::Session::DBI module. Tying these two together means that the default storage and retrieval mechanism associated with hashes no longer applies for %session—when we retrieve or modify its value, one or more methods in Apache::Session::DBI will take over:

eval {
    tie %HTML::Mason::Commands::session, 'Apache::Session::DBI',
        ($cookies{'AF_SID'} ? $cookies{'AF_SID'}->value() : undef),
         DataSource => $dbsource,
         UserName => $dbuser,
         Password => $dbpass

If this eval is unsuccessful, the variable $@ will contain the error message. Here, we test to see if the object exists in the data store. If so, then we assign the user a new session:

if ( $@ )
    if ( $@ =~ m#^Object does not exist in the data store# )
        tie %HTML::Mason::Commands::session,
             DataSource => $dbsource,
             UserName => $dbuser,
             Password => $dbpass
        undef $cookies{'AF_SID'};
Finally, if the user does not pass us any identifying AF_SID cookie at all, we create a new one and tell mod_perl to send it along with the rest of the outgoing headers:
if ( !$cookies{'AF_SID'} )
    my $cookie =
       new CGI::Cookie(-name => 'AF_SID',
                       -value =>
                       -path => '/',);
    $r->header_out('Set-Cookie', => $cookie);
Once these are in place, any Mason component can store and retrieve information in %session. Apache::Session's use of the Storable module means that references and complex data structures (such as arrays of arrays, and hashes of hashes) can be stored in %session without us having to worry about losing data.


Geek Guide
The DevOps Toolbox

Tools and Technologies for Scale and Reliability
by Linux Journal Editor Bill Childers

Get your free copy today

Sponsored by IBM

8 Signs You're Beyond Cron

Scheduling Crontabs With an Enterprise Scheduler
On Demand
Moderated by Linux Journal Contributor Mike Diehl

Sign up and watch now

Sponsored by Skybot