A third way of maintaining state is
to use Netscape persistent cookies. One of the features of the Netscape
Navigator browser is the capability to store information on the
client side. It does this by accepting a new Set-Cookie header from CGI programs,
and passing that information back using a HTTP_COOKIE environment variable.
We won't show a complete example, but we'll illustrate briefly.
A program that stores the information on the client side might
begin as follows:
#!/usr/local/bin/perl
($key, $value) = split(/=/, $ENV{'QUERY_STRING'});
print "Content-type: text/html", "\n";
print "Set-Cookie: $key=$value; expires=Sat, 26-Aug-95 15:45:30 GMT; path=/; domain=bu.edu", "\n\n";
The cookie header requires the key/value information to be
encoded.
The Set-Cookie header sets one cookie on
the client side, where a key is equal to a value. The expires
attribute allows you to set an expiration date for the cookie. The
path attribute specifies the subset of URLs that
the cookie is valid for. In this case, the cookie is valid and can
be retrieved by any program served from the document root hierarchy.
Finally, the domain attribute sets the domain
for which the cookie is valid. For example, say a cookie labeled
"Parts" is set with a domain attribute of "bu.edu".
If the user accesses a URL in another domain that tries to retrieve
the cookie "Parts," it will be unable to do so. You can also use
the attribute secure to instruct the browser
to send a cookie only on a secure channel (e.g., Netscape's HTTPS
server). All of these attributes are optional.
Now, how does a program access the stored cookies? When a
certain document is accessed by the user, the browser will send
the cookie information--provided that it is valid to do so--as the
environment variable HTTP_COOKIE. For example,
if the user requests a document for which the cookie is valid before
the cookie expiration date, the following information might be stored
in HTTP_COOKIE:
Full%20Name=Shishir%20Gundavaram; Specification=CGI%20Book
Cookies are separated from the next by the "
; " delimiter. To decode this information
and place it into an associative array, we can use the following
subroutine:
sub parse_client_cookies
{
local (*COOKIE_DATA) = @_;
local (@key_value_pairs, $key_value, $key, $value);
@key_value_pairs = split (/;\s/, $ENV{'HTTP_COOKIE'});
foreach $key_value (@key_value_pairs) {
($key, $value) = split (/=/, $key_value);
$key =~ tr/+/ /;
$value =~ tr/+/ /;
$key =~ s/%([\dA-Fa-f][\dA-Fa-f])/pack ("C", hex ($1))/eg;
$value =~ s/%([\dA-Fa-f][\dA-Fa-f])/pack ("C", hex ($1))/eg;
if (defined($FORM_DATA{$key})) {
$FORM_DATA{$key} = join ("\0", $FORM_DATA{$key}, $value);
} else {
$FORM_DATA{$key} = $value;
}
}
}
This subroutine is very similar to the one we have been using
to decode form information. You can set more than one cookie at
a time, for example:
print "Set-Cookie: Computer=SUN; path=/", "\n";
print "Set-Cookie: Computer=AIX; path=/images", "\n";
Now, if the user requests the URL in the path /images,
HTTP_COOKIE will contain:
Computer=SUN; Computer=AIX
There are a couple of disadvantages with this client-side
approach to storing information. First, the technique only works
for Netscape Navigator browsers. Second, there are restrictions
placed on the cookie size and number of cookies. The information
contained in each cookie cannot exceed 4KB, and only 20 cookies
are allowed per domain. A total of 300 cookies can be stored by
each user.