Even if you're running a framed site, you may find that you need to provide frame users the ability to turn off the frames for faster surfing. If you offer such an option, a nice addition for your site would be to have it "remember" that a particular user surfed without frames before, and then have it automatically return them to that mode the next time they visit. To pull this off, you need to be able to store information about the user. The Web mechanism that makes this possible is the persistent client-state HTTP object, more commonly referred to as a cookie.
When you run a program on your computer, it may store information (window placement, the name of the last file loaded, and so on) for use the next time you fire up the same application. The Web can do a similar trick, storing information sent from the server for use during a future browser session. These little "tidbits" of data are called cookies, and they can literally consist of anything: a user ID and password, the number of times a person has visited a site, the date and time of the user's last visit, and so on.
With cookies, a Web master can do these three things:
Cookies are initially sent from the server to the browser, and are stored in a file by the browser until the next time you surf by the same page. The next time you drop by and your browser requests the page from the server, it also sends the server any cookies associated with that page-or page tree, as you'll see later.
With the growing concern about security and information privacy on the Web, there is a good deal of misinformation about exactly what cookies can and cannot do.
Because cookies are designed to store browser-specific (or user-specific) data, they can help you with the following:
By themselves, cookies are not a security risk and cannot
Of course, if you were to provide any of the above to an HTML form, it's not outside the bounds of the script that processes the form to turn around and write much of that data as a collection of cookies back to your computer.
Even if you do provide such information to a server and
the server writes a cookie, the cookie is restricted (by design)
to be related only to the server that wrote it. In other
words, you can't write a server program that reads another server's
cookies.
CAUTION |
Some people recommend that you periodically delete the cookie file that your browser creates (for example, Navigator on the PC stores cookies in a file called cookies.txt in the same directory as the navigator.exe file) to ensure that sensitive information isn't stolen by unscrupulous servers. While this does no damage to your system (if the browser can't find the cookie file, it simply starts up a new one), it constantly puts you in the position of being a "new user" for many sites that rely on cookies to help configure their site to your tastes. Another tip is to "lock" your cookie file by making it read-only so no cookies can be written. This doesn't prevent cookies from being created, but it will prevent them from being saved. |
TIP |
If the thought of your server and browser exchanging information "behind your back" still bothers you, you can control cookies through a couple of different means-either through the browser directly or through a plug-in. Internet Fast Forward is a plug-in that installs into Navigator and can be used to prevent or monitor cookie transmissions. For control from within the browser itself, both Navigator 3.0 and Explorer 3.0 offer configuration options that allow the browser to warn you if a cookie is about to be exchanged and (optionally) not permit it. Be cautioned, though, that some sites will simply not permit you to go any further should you refuse to store their cookie, as some of them use the cookie as a security access key or a tracking flag. |
While most of the people who surf the Web use Navigator, Explorer, or Mosaic, there are still many other browsers out there, and not all of them support cookies. Also, because of licensing, there are customized versions of even the popular browsers, and some don't support cookies.
Digital Equipment Corp. has put together a script that tests your browser for cookie support, as well as displays the results of its tests on a rather broad selection of browsers. Here's where you can find its script:
http://www.research.digital.com/nsl/formtest/stats-by-test/NetscapeCookie.html.
Browsers aren't the only restrictions to cookie use; several servers in use don't support cookies, either. For a list of servers that do, or servers that require specific configuration, check out this Web site:
http://www.illuminatus.com/cookie_pages/servers.html.
Cookies are transmitted from the server to the browser within a document's header. If you were to look at the header block in transit, you'd see that a cookie has the following format:
Set-Cookie: name=value; expires=date; path=pathName; domain=domainName; secure
The five fields that make up a cookie are:
TIP |
While the name and value fields can contain any kind of data, it's recommended that you avoid spaces and special characters. If you need to embed spaces or special characters within a cookie, you should encode these characters using URL-style %XX coding, where characters are replaced with a percent sign (%) followed by their hexadecimal ASCII equivalent, such as %20 for a space. |
NOTE |
While Internet Explorer requires the entire date string (including the time) before it recognizes a cookie as valid, Navigator, on the other hand, can deal with cookies whose expire strings are as short as 01-Jan-99 GMT. |
NOTE |
Because of a bug in Netscape 1.1N, if you don't specify a path of at least "/" (the server root), the cookie won't get set. |
NOTE |
Requiring at least two periods keeps someone from creating a cookie that's good for all .com domains, for example. |
CAUTION |
Adding secure to the end of a cookie definition does not make the connection secure, it only keeps the cookie from being transmitted on a non-secure port. If you're not running a secure server (one that supports SSL) and you mark all your cookies as secure, none of them will be sent. |
Of the various fields in a cookie, only name=value must be defined. Additionally, some other things to remember about cookies include:
Now that you know what a cookie is and what it does, it's time to look at how to "bake" your own. Cookies can be created in several different ways:
From the server-side, probably the easiest way to create a cookie is to include a Set-Cookie header within the header block of an HTML object. The Set-Cookie header line you've already seen in the previous section:
Set-Cookie: name=value; expires=date; path=pathName; domain=domainName; secure
Listing 4.1 is an example of setting a cookie using the response header.
Listing 4.1 Set-Cookie
#!/usr/local/bin/perl ... print "Content-type: text/html\n"; print "Set-Cookie: myCookie=NewCookie; expires=07-Sep-99 GMT\n\n"; print "<HTML><BODY>Cookie Set</BODY></HTML>"; ...
NOTE |
Within an HTML object's header block, the order of the headers (Content-type, Set-Cookie, and so on.) isn't important. What is important is that the last header line has a blank line (an extra newline character) after it to inform the server that the header is finished and the object's body is coming next. |
Deleting a cookie from an object is just as easy-you simply "set" the cookie, but make the expiration date sometime in the past:
print "Set-Cookie: myCookie=NewCookie; expires=01-Jan-70 GMT\n\n";
NOTE |
Another way to delete a cookie is to "set" it, but leave the value attribute blank: print "Set-Cookie: myCookie=; expires=07-Sep-99 GMT\n\n"; Unfortunately, Internet Explorer doesn't like this technique. If you attempt to delete a cookie in this manner, Explorer leaves the cookie untouched. N |
The HTML <META> tag provides one mechanism for setting cookies. To set a cookie using a <META> tag, you'd employ the following syntax:
<META HTTP-EQUIV="Set-Cookie" Content="...">
where the Content attribute would contain the name, value, expires, domain, path, and secure fields.
The downsides of using the <META> tag, however, include:
Because of the restrictions with the <META> tag, using the Set-Cookie header is the preferred method.
Once you've created a cookie or two, reading the data back from within Perl is no different from reading in HTML form data-you work through an environment variable. In the case of cookies, the variable is HTTP_COOKIE, and pulling it from the environment retrieves every cookie that applies to the document, including any cookies that were created for documents in directories above the particular document.
Because individual cookie fields (and the cookies themselves) are separated by a semicolon and a space, the Perl fragment in listing 4.2 easily creates an array of cookie data.
Listing 4.2 Retrieving Cookie Data
#!/usr/local/bin/perl if(defined $ENV{HTTP_COOKIE}) { @cookieArray = split(/; /,$ENV{HTTP_COOKIE}) }
Once you've created your cookie array, scanning for a particular cookie is simple, as demonstrated in listing 4.3.
Listing 4.3 Getting a Cookie Value
# @cookieArray has been loaded previously # function GetCookie { $cookieName = ARGV[0]; $cookieValue = null; foreach(@cookieArray) { if($_ =~ /$cookieName/) { ($cookieName, $cookieValue) = split (/=/,$_) } $cookieValue; }
TIP |
Listing 4.2 demonstrates a format of the Perl foreach statement that might be unfamiliar to some, because it has no "item variable" like the following version: foreach $cookie (@cookieArray) { and instead uses the Perl special variable $_. $_ is the default pattern matching variable, and when no other variable is specified, is given the result of the pattern match. |
Because some browsers don't support cookies, having a little script that can identify whether a user's browser does or doesn't is a nice little treat. You can then quietly direct them to the appropriate part of your site (cookie or cookie-less), and it demonstrates another Perl trick in the process.
Listing 4.4 is an example of such a "cookie taster." It works by:
The reason I include this is because it is transparent to the browser; he or she just thinks that it takes a little bit too long for your first page to load.
Instead of printing a response, the taster.cgi could redirect browsers again to appropriate pages.
Listing 4.4 Cookie Taste Test
#!/usr/local/bin/perl $me = 'taste.cgi'; if($ENV{'QUERY_STRING'} eq 'TEST') { if($ENV{'HTTP_COOKIE'} =~ /Cookie=Test/) { $newDoc = "cookieDoc.html"; } else { $newDoc = "noCookies.html"; } print "Location: $newDoc\n\n"; } else { # print "Location: $me?TEST\n"; print "Set-Cookie: Cookie=Test\n\n"; print "<HTML><BODY></BODY></HTML>"; }
NOTE |
This will not work on all servers, because some servers optimize the header information by putting all header lines on one physical line by removing the newline characters between individual header fields. According to the HTTP specification, this is valid, but Netscape Navigator won't recognize a Set-Cookie directive unless it's on a line of its own. For a list of servers that handle cookies properly (and those that don't, and why), check out: http://www.illuminatus.com/cookie_pages/servers.html. |
The cookie property of the document object is the JavaScript wrapper for the cookie interface. Just as cookies are a very long string in Perl, in JavaScript the cookie object is of the string type. Therefore, the manipulations to create, delete, and read cookies are very similar to their Perl counterparts.
Listing 4.5 is a JavaScript function that creates a cookie. It takes advantage of a JavaScript function's ability to handle more parameters than are defined by testing the arguments property of the function.
Listing 4.5 Setting a Cookie with JavaScript
function SetCookie(name, value) { var argv = SetCookie.arguments; var argc = SetCookie.arguments.length; var expires = (argc > 3) ? new Date(argv[3]) : null; var path = (argc > 4) ? argv[4] : null; var domain = (argc > 5) ? argv[5] : null; var secure = (argc > 6) ? argv[6] : false; document.cookie = name + "=" + escape(value) + ((expires == null) ? "" : ("; expires=" + expires.toGMTString())) + ((path == null) ? "" : ("; path=" + path)) + ((domain == null) ? "" : ("; domain=" + domain)) + ((secure == true) ? "; secure" : ""); }
You use this function as follows:
SetCookie(name, value [, expires, path, domain, secure]);
where "name," "value," "expires," "path," "domain," and "secure" correspond to the previously introduced cookie components. Note that the last four parameters are optional (as indicated by the square brackets). For example, to set a cookie named count to the number of times a user has visited your site, you could call SetCookie() as follows:
SetCookie("count", "5");
which would make the count cookie available to all the pages on your site because "domain" and "path" revert to their default values. The cookie itself, because the expires property wasn't specified, would exist only until the user closes his or her browser.
Deleting a cookie through JavaScript is no different from deleting a cookie in Perl-simply set the cookie's expires parameter to a time in the past.
Listing 4.6 demonstrates retrieving cookies through JavaScript. As with Perl, you scan through the cookie string looking for the substring name=, where name is the desired cookie. If the substring is found, everything after the equal sign and before the next semicolon will be the cookie's value.
Listing 4.6 Retrieving Cookies with JavaScript
function GetCookie(name) { var arg = name + "="; var alen = arg.length; var clen = document.cookie.length; var i = 0; while(i < clen) { var offset = i + alen; if(document.cookie.substring(i, offset) == arg) { var iEnd = document.cookie.indexOf(";", offset); if(iEnd == -1) { iEnd = document.cookie.length; } return unescape(document.cookie.substring(offset, iEnd)); } i = document.cookie.indexOf(" ", i) + 1; if(i == 0) { break; } } return null; }
Internet Explorer supports cookies, but not from within JScript, Microsoft's name for its implementation of JavaScript. Fortunately, getting around this is relatively easy, if all you intend to use cookies for is keeping track of things during the current visit to your site.
Basically, you "wrap" the cookie functions with a browser test:
if(navigator.appName.indexOf("Netscape") != -1) { // Safe to use cookie object } else { // no cookie object, go to Plan B }
Even though Explorer doesn't support cookies, you can still make use of them by doing the following:
This is actually easier than it sounds, thanks to the JavaScript eval() function, which takes its parameter and evaluates it as though it were a JavaScript statement. This means that
eval("myGlobal=10");
would set the global variable myGlobal to 10. This makes it possible to keep the cookie functions generic. Listing 4.7 is a code fragment that takes the GetCookie() and SetCookie() functions and sets them up to work within Explorer.
Listing 4.7 Explorer Cookies
function GetCookie(name) { if(navigator.appName.indexOf("Netscape") != -1) { // GetCookie manipulation code } else { return eval(name); } } function SetCookie(name, value) { if(navigator.appName.indexOf("Netscape") != -1) { // SetCookie manipulation code } else { eval(name + " = '" + value + "'"); } }
NOTE |
It's important to point out that this technique works only if you've centralized your source code into a top-level frame. Once a page is unloaded, all the "cookie" data is lost. However, if you've located your JavaScript code within the parent document of your site, this trick has the same effect as creating cookies that last only for the duration of the user's browsing session-except it's limited to your site instead of the entire browser session. |
This chapter presents a brief introduction to cookies, the mechanism by which you can store client-specific data on a user's computer. They can be helpful for many things, such as online ordering systems. An online ordering system could be developed using cookies that would remember what a person wants to buy-this way, if a person spends three hours ordering CDs at your site and suddenly has to get off the Internet, he or she could quit the browser and return weeks or even years later and still have those items in his or her shopping basket.
Site personalization is another use for cookies. This is one of the coolest uses. Suppose a person comes to your site but doesn't want to see any banner advertisements. You could allow him or her to select this as an option and from then on until the cookie expires, he or she wouldn't see them.
Also, using a cookie in conjunction with a server-side script to store the information for tallying to track the number of visits (or hits) to your site, or number of times a single person visits.
For more information on related topics, check out: