< Previous Session | Next Session >

Session Eight
Other Topics: Cookies

Cookies are a general mechanism which server side CGI programs can use to both store and retrieve information from the client side of the connection. They are a simple mechanism for creating a persistent state between a web server and a web client. The addition of this mechanism significantly extends the capabilities of Web-based client/server applications.

A cookie is a unique piece of information that is shared between a web server and a web browser. Your web browser saves its part of the cookie as a plain text file on the computer's hard drive [1]. You can do many useful things with cookies. If your site requires registration, you can set cookies to store the user's login name and password. Another use for a cookie might be to keep track of the pages that a user visits and count the number of visits.

A cookie always included the address of the server that sent it. When a visitor returns to your server, it will query the browser to see if the visitor is a cookie holder. If so, the server will retrieve the information stored in the cookie.

The Set-Cookie Response Header

A cookie is introduced to the client by including a Set-Cookie header as part of an HTTP response, typically this will be generated by a CGI program. The following is the format that a CGI program would use to add a HTTP header that will be stored by the client as a cookie.

   
   Set-Cookie: NAME=VALUE; expires=DATE; path=PATH; domain=DOMAIN_NAME; secure

NAME=VALUE
This string is a sequence of characters excluding semi-colon, comma and white space. If there is a need to place such data in the name or value, some encoding method such as URL style %XX encoding is recommended. This is the only required attribute on the Set-Cookie header.

expires=DATE
The expires attribute specifies a date string that defines the valid life time of that cookie. Once the expiration date has been reached, the cookie will no longer be stored or given out. The date string is formatted as:

Wdy, DD-Mon-YYYY HH:MM:SS GMT

The only legal time zone is GMT and the separators between the elements of the date must be dashes. "expires" is an optional attribute. If not specified, the cookie will expire when the user's session ends.

domain=DOMAIN_NAME
When searching the cookie list for valid cookies, a comparison of the "domain" attributes of the cookie is made with the Internet domain name of the host from which the URL will be fetched. If there is a tail match, then the cookie will go through "path" matching to see if it should be sent. "Tail matching" means that "domain" attribute is matched against the tail of the fully qualified domain name of the host. A "domain" attribute of "acme.com" would match host names "anvil.acme.com" as well as "shipping.crate.acme.com".

The default value of domain is the host name of the server which generated the cookie response.

path=PATH
The path attribute is used to specify the subset of URLs in a domain for which the cookie is valid. If a cookie has already passed domain matching, then the pathname component of the URL is compared with the path attribute, and if there is a match, the cookie is considered valid and is sent along with the URL request. The path "/foo" would match "/foobar" and "/foo/bar.html". The path "/" is the most general path.

If the path is not specified, it as assumed to be the same path as the document being described by the header which contains the cookie.

secure
If a cookie is marked secure, it will only be transmitted if the communications channel with the host is a secure one. Currently this means that secure cookies will only be sent to HTTPS (HTTP over SSL) servers. If secure is not specified, a cookie is considered safe to be sent in the clear over unsecured channels.

The Cookie Request Header

When requesting a URL from a Web server, the browser will match the URL against all cookies and if any of them match, a line containing the name/value pairs of all matching cookies will be included in the HTTP request. Here is the format of that line:

   
   Cookie: NAME1=OPAQUE_STRING1; NAME2=OPAQUE_STRING2 ...

If a CGI process is generated by this request, the server will put this line into an environment variable that is named HTTP_COOKIE.

Additional Notes

Examples

Here are some sample exchanges which are designed to illustrate the use of cookies.

First Example transaction sequence:

Client requests a document, and receives in the response:
Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT

When client requests a URL in path "/" on this server, it sends:
Cookie: CUSTOMER=WILE_E_COYOTE

Client requests a document, and receives in the response:
Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/

When client requests a URL in path "/" on this server, it sends:
Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001

Client receives:
Set-Cookie: SHIPPING=FEDEX; path=/foo

When client requests a URL in path "/" on this server, it sends:
Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001

When client requests a URL in path "/foo" on this server, it sends:
Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001; SHIPPING=FEDEX

Second Example transaction sequence:

Assume all mappings from above have been cleared.

Client receives:
Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/

When client requests a URL in path "/" on this server, it sends:
Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001

Client receives:
Set-Cookie: PART_NUMBER=RIDING_ROCKET_0023; path=/ammo

When client requests a URL in path "/ammo" on this server, it sends:
Cookie: PART_NUMBER=RIDING_ROCKET_0023; PART_NUMBER=ROCKET_LAUNCHER_0001

NOTE: There are two name/value pairs named "PART_NUMBER" due to the inheritance of the "/" mapping in addition to the "/ammo" mapping.

dBASE Plus and Cookies

There are two steps to using cookies in your dBASE Plus Web applications. First, you must set the cookie on the browser; second you must read the cookie when the browser calls your application.

Set the Cookie. We will begin by setting the cookie for the browser. This is done in the CGI header with a "Set-Cookie:" line.

    
   Set-Cookie: cookieName=cookieValue;expires=expirationDateGMT

Let's do this in a program file. In the program we will create a variable with a UserID and a date for when the cookie expires. Then we will stream a response page. That page will contain the command to set the cookie and write a short message in the browser. The following code should be inserted into your Web application template. Notice that the expires date is created by using the "toGMTString" method from dBL date classes. This method converts the local date to GMT and returns a string. This is the format required by a Cookie.

   
   d = new date( dtoc(date()+10) )
   d1 = d.toGMTString()
   cUserid = 'nuwermj'

   Cookie = "UserID=" + cUserid + "; expires=" + d1
      
   oCGI.fOut.Puts("Set-Cookie: " + Cookie)
   oCGI.fOut.Puts("Content-type: text/html")
   oCGI.fOut.puts('')
   oCGI.fOut.Puts("<HTML>")
   oCGI.fOut.Puts("<HEAD>")
   oCGI.fOut.Puts('<META HTTP-EQUIV=Content-Type CONTENT="text/html">')
   oCGI.fOut.Puts("</HEAD>")
   oCGI.fOut.Puts(" ")
   oCGI.fOut.Puts([<BODY>])
   oCGI.fOut.Puts([The cookie <p>] + Cookie + [<p>has been set.])
   oCGI.fOut.Puts("</BODY>")
   oCGI.fOut.Puts("</HTML>")

Compile, build and then execute this program. (My copy of the program is in the file named "Exercise0801a.prg.") The string that is the cookie should be displayed in the response page.

 
Note

You can create a cookie and at the same time use the WebClass streaming methods. The "Set-Cookie:" header line is the very first string streamed to the browser. As you can see in the example above, the next string is the "Content-type:" header line. This lets us stream the "Set-Cookie:" header line first, and use the WebClass's streamHeader() method to send the rest. In other words, the following will work in your web program.

   
   oCGI.fOut.Puts("Set-Cookie: " + Cookie)
   oCGI.steamHeader()

You can alternatively, create your own custom streamHeader() method that contains a "Set-Cookie" header line. Your custom method would then override the default method just like an overridden streamBody method.


Retrieving Cookies. Once a cookie is set at the browser, you will want to retrieve it for use in your web application. We will use a custom method for this purpose. Bowen Moursund, the author of this method, posted it in the dbase Newsgroup. The method retrieves and formats the CGI environment variable that contains the HTTP cookie. The method is named "LoadArrayFromHTTP_COOKIE" and it is contained in the WebClassEx library.

We will use this method in our next program. Create a new CGI program file; be sure to use the WebClassEx library; and, after, the call to the connect() method, add the following lines:

   
   oCGI.LoadArrayFromHTTP_COOKIE("UserID")
   cMsg = 'UserID is: ' + oCGI.dBCookie['UserID']
   
   oCGI.streamHeader()
   oCGI.streamBody( cMsg )
   oCGI.streamFooter()

This is a simple example, which retrieves the UserID from the cookie and streams that value back to the browser so that you can see it.

Compile, build and execute this program. The program file that I created is named "Exercise0801b.prg." The response page will display the UserID contained in the cookie.

A Cookie name/value pair must never have spaces, commas, or semicolons. In the event that your cookie might contain one or more of these characters, you should encode the cookie string with the escapeCookie() method contained in the WebClassEx library.

   
   cCookie = cookieName+"="+cookieValue
   cCookie = oCGI.escapeCookie( cCookie )

The LoadArrayFromHTTP_COOKIE() method is already designed to decode cookie values when they contain any escape charecters, so you don't need to worry about that piece.

Another issues that you must be aware of is that, when the cookie methods contained in the WebClassEx library are used, the pipe ("|") and equals ("=") characters must never be used in the cookie name or value. The dBASE cookie methods adopt a particular technique for handling multiple name/value pairs with a single cookie. The technique is used by other languages as well, but it's important to understand the data structure. The format of a dBASE cookie is:

    
   cookieName = name1=value1|name2=value2|name3=value3

The string to the right of the first equals sign is the HTTP cookie value. This value string contains a set of name/value pairs separated by the pipe ("|") character. LoadArrayFromHTTP_COOKIE() is designed to parse this data, which is why dBASE cookies do not permit the "=" or "|" character in names or values. Thus, when you want to store multiple values in a cookie, you would construct a cookie value like this:

   
   myCookieValue = name1 + "=" + value1 + "|" + name2 + "=" + value2

And then assign this value to the cookieName

   
   cookieName=myCookieValue

This technique for formatting a cookie permits the packaging of multiple values into a single cookie. If, however, your cookies contain only one value, then the format can be a normal name/value pair:

   
   CookieNAME=CookieVALUE

LoadArrayFromHTTP_COOKIE() is designed to handle a single name/value pair or multiple name/value pairs.


[1] Portions of the following have been adapted from "Persistent Client State HTTP Cookies"
http://developer.netscape.com
< Previous Session | Next Session >

The Legal Stuff: This document is part of the dBASE onLine Training Program created by Michael J. Nuwer. This material is copyright © 2001, 2003 by Michael J. Nuwer. dBASE is copyrighted, trademarked, etc., by dBASE, Inc. This document may not be posted elsewhere without the explicit permission of the author, who retains all rights to the document.