Using Netscape Cookies with Powersoft’s Web.PB
 

By Don Draper

One difference between web applications traditional client-server applications is that web applications utilize a stateless connection. Instead of maintaining a constant connection between the web browser (client) and web server, the server provides a connection just long enough to send over an HTML page (or other data) and then they close or disconnect from the web browser. Each time your Web.PB application is invoked, it has no way of knowing what happened on the previous connection. This makes it difficult to track users as they navigate around your web site. For example, you may want your users to login to your web application only once but still be able to check their security level as they access various areas of your site. Or perhaps you want to store a user’s shopping list as they fill their basket in your web store.

Powersoft’s Internet Developer Toolkit provides a technique for state management via a powerful class library that uses database access and hidden fields within web pages. However, this involves significant overhead and is often more power you need. This is where "cookies" come into the picture.

Web.PB/Distributed PowerBuilder (DPB) can use cookies to both store and retrieve information on the client browser that the server can process. The data can be persistent on the client-side, greatly extending the capabilities of web-based applications to track users. Unfortunately, the Internet Development Toolkit does not directly support cookies for state management as most other tools do including NetImpact Dynamo. However, this article will show just how easy it is to add cookies to your Web.PB applications.

Overview

A "cookie" is a small piece of information that a web server application may store temporarily on a client computer (browser). This permits the client to store specific information that the same web server can later retrieve. Using cookies provides another method of state management in the form of client-side, persistent information. Cookies were created and first implemented by Netscape and later adopted by Microsoft’s Internet Explorer. Although these browsers are very ubiquitous, you must keep in mind that cookies will not work for tracking users who use browsers that do not support them.

As you browse the web, cookies sent to your Netscape Navigator or Microsoft Internet Explorer are stored in memory. When you exit these browsers, any cookies that have not expired are written to a file so they may be accessed the next time you run this browser against the same server. For example, Netscape Navigator creates a file named "cookies.txt" where cookie data is stored. You may open this file in notepad to view it.


Sending the Cookie to the Client

A cookie is typically created in a web application and transmitted to the client by including a "Set-Cookie" header as part of an HTTP response. Web.PB supports cookies since the Set-Cookie HTTP header may be returned to the browser from a DPB method when the return data type is a blob. The data stored in the client cookie may then be retrieved from the client as an environment variable. We’ll see how this is done later on in the article.

Cookies are created by your CGI program (a DPB method using Web.PB) by sending a "Set-Cookie" HTTP header line back to the client browser in response to a URL request. The Set-Cookie header is actually made up of several fields, each with a name and value. The most important of these is the name and value of the data you wish to store. For example, USERID=12545 or ACCESSLEVEL=Admin. Other names and values represent attributes that determine when the cookie expires, which server domain is permitted to retrieve the cookie and more.

Here is a sample HTTP cookie header:

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

An HTTP header is simply a single line command separated from other HTTP headers by a single carriage return / line-feed and from the regular data by a pair of carriage return / line-feeds. For example, the basic layout of an HTML page might really look like:

Set-Cookie: USERID= 123;
Set-Cookie: USERNAME=Bill
<HTML>
<BODY>
Welcome to my home page
</BODY>
</HTML>

Notice the blank line between the last header and the html page. This is really a pair of carriage return / line-feeds and is easy to forget to include. Also note that multiple "Set-Cookie" headers may be returned in a single response. A browser may begin to purge older cookies if its limit is reached.

NAME represents the name of the value to be stored on the client. VALUE is the data to be stored such as a user’s identification number or security access level. DATE is the date and time at which the cookie expires and uses the generic format of Wdy, DD-Mon-YYYY HH:MM:SS GMT. Expired cookies may not be retrieved by the server and will be removed from the cookie file once the browser is closed.

DOMAIN is the domain name of the server from which the cookie data originated. A server may retrieve only those cookies on a client where the cookie domain value matches the server’s domain. This prevents others web servers from accessing cookies which they did not send.

PATH indicates a URL or portion of a URL from the server for which the cookie is valid. The URL (or a portion of it) must match before the server can retrieve the cookie. Typically the PATH is set to "\" so that any URL from the same server will be able to retrieve cookie records.

The "secure" keyword may be added to the cookie header to force the browser to transmit the data using a secure socket (SSL) connection. This adds additional security for sensitive cookie data. All fields are optional except NAME=VALUE which must be present.

Recall that DPB methods are where code is written to process requests from browsers and to respond with data. When DPB methods return data to Web.PB and subsequently to the browser, the return data type of the DPB method is very important. Methods that return strings to Web.PB signal Web.PB to prefix the data with an HTTP header of "Content-type: text/html". Therefore the HTTP header for the cookie cannot be added by the DPB method. The DPB method must return a data type of blob. The DPB method may then prefix and necessary headers including the "Set-Cookie" header.

Retrieving the Cookie from the Client

We now know how to get data from the Web.PB application and store it on the client. But how does the Web.PB application request cookie data from the client once it is there? The answer is that it is automatic. All cookies on the client whose DOMAIN and PATH match the server’s URL will automatically be returned to the Web.PB application as an environmental variable. This variable is called HTTP_COOKIE and must added to the appropriate section of the pbweb.ini file so that Web.PB will know to include it.

For example, if we were using regular CGI to call Web.PB, then we would add a new CGI environmental variable named HTTP_COOKIE into the CGI Keywords section of the pbweb.ini file.

CGIKeywords="SERVER_SOFTWARE, SERVER_NAME, GATEWAY_INTERFACE, SERVER_PROTOCOL, SERVER_PORT, REQUEST_METHOD, HTTP_ACCEPT, PATH_INFO, PATH_TRANSLATED, SCRIPT_NAME, QUERY_STRING, REMOTE_HOST, REMOTE_ADDR, REMOTE_USER, CONTENT_TYPE, CONTENT_LENGTH, HTTP_USER_AGENT,HTTP_COOKIE"

Next, simply add an additional argument to the DPB method name HTTP_COOKIE with a data type of string. Any arguments who’s name matches a supported environment variable will receive the value of that variable from the server. In this case, the HTTP_COOKIE argument receives the value of any matching cookie entries on the client. All matching cookies will be appended to the HTTP_COOKIE argument string in the form of:

Cookie: NAME=VALUE; NAME=VALUE;


Now let’s look at some actual PowerBuilder code which demonstrates some of the techniques we have discussed. The following code shows a DPB method which receives an argument named HTTP_COOKIE and also creates the cookie by returning the "Set-Cookie" HTTP header as a blob return type. The code is slightly modified from a Powersoft example.

string ls_html
string ls_cookie
int li_counter,li_quantity,li_rowCount
int li_find , li_end
blob lblob_response

// Check to see if data transfer is complete, if so return Null blob.

IF ib_TerminateProcessing = TRUE THEN
   
setNull(lblob_response)
  
return lblob_response
end if

string ls_HTTPHeader="content-type: text/html~r~n~r~n" // note the CRLF pairs after last header entry
string ls_cookie1 = 'Set-Cookie: timesin='
string ls_cookie2 ='; expires=Friday, 05-23-1997 08:00:00 GMT; path=/cgi-shl/~r~n'
int li_entered_count

IF LEN(TRIM(http_cookie)) > 0 then
    li_find = pos(http_cookie,'timesin=')
    li_find = li_find + 8
   
li_entered_count = integer(mid(http_cookie,li_find))
   
li_entered_count =li_entered_count + 1
   
ls_html = ' <h1> you have been to this site ' + string (li_entered_count) + ' times !! </h1>'
else
   
li_entered_count = 1
   
ls_html = ' <h1> this is your first visit to the site </h1>'
end if

ls_cookie = ls_cookie1 + string(li_entered_count) + ls_cookie2

// Prevent second execution of this function. Web.PB continues to call the method until a null or 0 length
// blob is returned.

ib_TerminateProcessing=True

// Return HTML Page
lblob_response = blob( ls_cookie + ';' + ls_HTTPHeader + ls_html)

//lblob_response = blob( "Set-Cookie: PBCOOKIE=GOOD; expires Thursday, 22-May-1997 GMT;~r~n" + ls_HTTPHeader + ls_html)
return lblob_response

This following HTML may be used to call the DPB method above.

<HTML>
<H2>This is an example of using Cookies and Web.pb.</H2>
<A HREF="/cgi-shl/pbcgi050.exe/idkdemo/nvo_cookie_test/f_cookie?">Call URL for the DPB method using cookies</A>


The idkdemo is the section in the pbweb.ini file that contains the connection information to the DPB application server. The nvo_cookie is the non-visual object that holds the DPB method. Finally, the f_cookie is the actual name of the DPB method being called.

The first time the method is called, the HTTP_COOKIE environmental variable will be null and the "Set-Cookie" HTTP header string is built containing a name of ‘timesin’ with an initial value of 1. Note that this header is also followed by a second "Content-type" HTTP header and the two headers themselves are separated by a single carriage return/line feed. Also note that the second (last) header is followed a pair of carriage return/line-feeds to separate the headers from any following data. Forgetting to add the two carriage return/line-feeds are a common error the Common Gateway Interface (CGI) programmers make.

The headers and html are then returned to the browser via Web.PB where the browser will see the "Set-Cookie" HTTP header and store the cookie on the client machine. Subsequent calls to the method by clicking on the URL link created by the HTML will result in the browser passing the cookie data back as a string contained within the HTTP_COOKIE argument. This argument will now contain the number that was stored on the client, be parsed and incremented, and then restored as the "Set-Cookie" header gets rebuilt and sent to the browser again.

NOTE: The instance boolean variable of ib_TerminateProcessing is needed to signal the second call to the method and will return a null blob to Web.PB. This is because Web.PB will continue to call the same method repeatedly until the method returns a null blob.

Hopefully this brief look at "Client Cookies" will have you performing some great state management on your web site using Web.PB and cookies! Visit the Netscape web site more the complete specification on using client cookies at www.netscape.com.

Back to Developer Resources