The other main client-side technique we'll consider uses HTTP cookies to store state information. HTTP cookies are named bits of information that are transmitted between the server and browser within the HTTP header. Ordinarily the server creates a cookie by including a Set-Cookie field in the HTTP header. The browser then stashes away the cookie information in a small in-memory or on-disk database. The next time the browser makes a request from that particular server, it returns that cookie in a Cookie field.

Cookies are relatively flexible. You can create cookies that will be returned to only one specific server or to any server in your domain. You can set them up so that they're returned only when users access a particular part of the document tree or any URI in the document hierarchy. They can be set to expire immediately when the user exits the browser, or they can be made to persist on the user's disk database for an extended period of time. You can also create secure cookies that are only returned to the server when a secure protocol, such as SSL, is in effect. This prevents cookies from being intercepted in transit by network eavesdroppers.

The exact format of HTTP cookies is somewhat involved and is described in
the HTTP specification at http://www.w3.org/Protocols.
Fortunately it's easy to make cookies in the right format using the CGI::Cookie
module. To create a cookie with the name Hangman, a value equal
to the hangman state variable $state, and an expiration time one
month from now, you would call CGI::Cookie::new() in this
way:

You can now send the cookie to the browser among the HTTP header fields using the -cookie argument to CGI.pm's header() method as shown here:

print header(-cookie => $cookie);

On subsequent invocations of the program you can retrieve named cookies sent by the browser with CGI.pm's cookie() method:

%cookie = cookie('Hangman');

Note that CGI.pm allows you to set and retrieve cookies that consist of entire hashes.

If you want to bypass CGI.pm and do the cookie management yourself within the Perl Apache API, you can use CGI::Cookie to create and parse the cookie format and then get the cookies in and out of the HTTP header using the Apache header_in() and header_out() methods. The experimental Apache::Request module also has cookie-handling functions.

Using the Perl Apache API, here's how to add a cookie to the HTTP header:

$r->header_out('Set-Cookie' => $cookie);

Here's how to retrieve and parse the cookies from the HTTP header and then find the one named Hangman:

Because we already require it for the hangman game, we'll use the CGI.pm shortcuts
for cookie management. We only need to make a few changes to reimplement the
hangman game to use cookies for state maintenance. The updated subroutines are
shown in Example 5-2.

At the top of the file, in addition to importing functions from CGI.pm, we bring in the CGI::Cookie module. This isn't strictly necessary, since CGI.pm will do it for us, but it makes the code clearer. We retrieve the state as before by calling get_state(), but now we do it only if the CGI parameter clear is not defined. We'll see why we made this change later.

Next, having retrieved the state, we (re)initialize it if necessary in order to choose a fresh word at the beginning of a new game. We process the user's guess by calling process_guess() and then print out the HTTP header. Here's where we find the first big difference. Instead of sending the state information to the browser within the HTML body, we need to save it in the HTTP header. We call save_state() in order to create a correctly formatted cookie, then send it down the wire to the browser by passing it to CGI.pm's header() method as the value of the -cookie argument.

Turning our attention to the pivotal get_state() and save_state() functions, we see that get_state() calls CGI.pm's cookie() method to retrieve the value of the cookie named Hangman (stored in the constant COOKIE_NAME). cookie() takes care of flattening and expanding arrays and hashes for us (but not more complex structures, unfortunately), so we don't need to copy any fields to a separate $state variable, we just return a reference to the cookie hash itself! Similarly, in save_state(), we just turn the entire state structure into a cookie by passing it to CGI::Cookie::new(). We specify an expiration time of one month in the future (+1M). This allows the cookie to persist between browser sessions.

Because we don't have to mess around with hidden fields in this example, the
show_guess_form() subroutine doesn't need to call save_state().
Likewise, we can remove the call to save_state() from show_restart_form().
The latter subroutine has an additional modification, the addition of a checkbox
labeled "Clear scores" (see Figure 5-2). If the user
selects this checkbox before pressing the new game button, the program clears
out the state entirely, treating get_state() as if it returned an undefined
value.

Figure 5-2. The improved version of the hangman
game allows users to clear their aggregate scores and start over.

The rationale for this feature is to capitalize on a bonus that you get when you use persistent cookies. Because the cookie is stored on the user's disk until it expires, the user can quit the browser completely and come back to the game some days later to find it in exactly the state he left it. It's eerie and wonderful at the same time. Of course, the user might want to start out fresh, particularly if he hasn't been doing so well. The "Clear scores" checkbox lets him wipe the slate clean.