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 cnn.com, my browser will return only that cookie—which is, after all, simply a name-value pair—when I visit http://www.cnn.com/ 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 handler.pl (but which I prefer to call mason.pl), 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 mason.pl also ensures that it is available in all Mason components. See Listing 1 for a simple example of mason.pl 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,
'Apache::Session::DBI',
            undef,
            {
             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 =>
                       $HTML::Mason::Commands::session{_session_id},
                       -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.

______________________

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState