Interactive Programming With CGI

The next step: interactive games using CGI. In this multipart series, John and Robert provide a framework for writing your own CGI games.

by John W. Fawcett and Robert W. Jepson

As most of you are probably aware, the World Wide Web is expanding at an unbelievable rate. So many people have discovered that they can use the Web as a publishing and promotional tool that it's now possible to spend months at a time on-line and never visit the same site twice. Unfortunately, most of these sites are pretty dull. Static text with static pictures and static hyper-links are nice for sitting and reading information, but so is a trip to the library. What the Web needs is interactivity! Programs that actually do something, reacting to those button clicks--programs that give customized response depending on the user and the information the user supplies!

It is possible to provide interactivity for the Web. Using the Common Gateway Interface (CGI), programmers can create entire systems that make the user forget that they're operating within the restrictions of HTML and feel more like they're interacting with their own personal copy of your program.

Most CGI ``programming'' to date has been restricted to passing information from the web page to UNIX filters like perl and awk, where it is operated on to create a new page, and this new page downloaded. This approach is fine for simple tasks that don't require tracking information across multiple pages, but it falls apart quickly when you try to tackle more complex tasks. Pretty quickly, you get to the point where you find that your concerns for speed, unique requirements, or access to a database require that you create a custom program on the back end.

Fortunately, CGI can support custom programs. In fact, with careful programming, you can create the illusion that the user is actually interacting with your program the same way they interact with any program running locally on their machine. This illusion of interactivity requires careful design and some clever programming to pull off, though, since there are a number of rather serious restrictions to working with CGI over the Web.

This article will discuss some of the problems associated with creating interactive programs with CGI. We'll draw on our experience as designers and programmers on the Sierra On-Line game Internet Stock Market Challenge, which is an interactive, stock trading simulation game. If you want to take a look at the game, link to http://www.sierra.com and follow the link to the Stock Market Challenge game. We're going to assume that you're familiar with basic HTML and HTML forms. If you're not, there is an excellent reference at http://www.w3.org/hypertext/WWW/MarkUp/MarkUp.html.

Design Considerations

One of the first steps in creating any program should be the design. In this, CGI programs are no different, but they have some special restrictions that need to be addressed at the beginning, and considered throughout the development lifecycle.

The first thing you'll need to do is design the pages that the user will see. For Stock Market, we used static HTML documents to let us get an idea of our page layout, the words we wanted to use on the page, and the features of the game we needed and were able to support. These static pages didn't actually do anything, but they let us (1) design the user interface and (2) get a handle on the paths the users might take through the system. When we started, we only had a few pages defined. By the time we got to actually writing the code, we had over 20.

You'll also need to remember that CGI only works with forms. Most of the forms you've seen to date have probably had a Submit button on them somewhere. That button was used when you'd finished filling in the form and wanted to send it to the host computer.

Buttons aren't the only submit device; Listing 1 shows the HTML code that produces Figure 2 (the page returned to the user which contains a bitmapped button bar). Listing 2 is the chunk of code that handles figuring out what section of the button bar was pressed. If you use the tag:

<INPUT TYPE=IMAGE SRC={.GIF file} NAME={bitmap name}>
in your document, the user will send the form whenever they click in the bitmap. You can extract the X and Y locations of their click from the data passed into stdin from the HTTP server. This is the method we used to create ``button rows'' that allowed the user a great deal of navigational flexibility when using Stock Market Challenge.

Managing Persistent Information

When you select a link on your browser, what really happens? You know that when a link on a document is activated, either the entire document pointed to is sent directly from the server, or the program pointed to is started on the server. But what really happens on the server? The answer has a profound impact on your design. For the purposes of this discussion, we'll only deal with what happens when the link points to a program.

When you activate the link, a request gets transferred over the Internet to the TCP/IP service on the target computer (server). The TCP/IP service routes the request to the correct port (typically port 80), where the HTTP server is attached. The HTTP server examines the request and launches a copy of the program pointed to (your program), with the stdin and stdout for that program pointing to sockets that are connected to the client browser. Your program reads from stdin to get data and writes information to stdout. Assuming that the information written out is in standard HTML format, the results are displayed on the users machine. Your program then exits.

Once the HTTP service activates your program, it waits for another connection. Your program can run as long as it needs to without preventing other users from getting into the system with requests of their own. This means, of course, that you might have two or more copies of your program running at the same time.

Back in the old days, we called this Block Mode operation. If you're really old, you probably remember card decks as data input devices. You'd create your card deck, submit it for processing, then examine the results. This is actually pretty close to the way you're going to operate with CGI and the Web. Welcome to 1972.

Remember that your program exits after you write the page back to the user. How, then, do you create the illusion of interactivity? How do you know which user is sending which page to your program?

The answer is the HIDDEN input type. You can use this HTML tag to place information on the page that the user never sees, but is passed to your program when the user activates the form. Be careful about what you place in the hidden fields. You need to place enough there to reconstruct the entire user context, but you also want to keep the amount of information to a minimum to prevent long transmission times. In Stock Market Challenge, every page has at least 4 items. These items (the user ID, the challenge ID, the number of the current page, and the number of the page to return to if this page causes an error) are enough to get started decoding the rest of the form. The main program for Stock Market Challenge consists primarily of a switch statement based on the current page ID. The routines called from that switch know what information they expect to be on the page, and decode it separately.

Think about the persistent information up front during the design period. What items are needed all the time? What items are common to groups of pages? What items are specific to only one or two pages? Remember to allow for error recovery and help. If there is an error, how do you get back to the page the user was on before the error occurred (after you tell them about the error, of course)? Your help needs to be specific to a page, and the user will expect to go back to the page that they caused the error on after they have read the help.

You might be tempted to place standard hyper-links for help and error pages. Don't. Of all the browsers we've checked out so far, only the most recent version of Netscape retains enough information to allow the use of the previous and next arrow buttons for navigation with CGI pages. All the rest seem to lose some critical information, meaning that the user has to go back to the beginning after getting help. Instead, treat help and error just like any other page--create them on the fly and use the HIDDEN input type to store the critical navigation information.

Choosing A Host Platform

The host platform for your program isn't as important as it used to be. Your code must read from stdin and write to stdout. The Stock Market Challenge code was first developed on a Linux system, then moved to a Windows NT environment, then moved to a mixed Windows NT/Sun environment. Because we stayed with standard C and C++ when we wrote the code, it moved around with very little modification. Choose the development environment that you feel most comfortable with. Write portable code. Do all the things that good programmers do. You shouldn't have any major problems.

Identifying User Actions

The next major issue you'll face is how you get user information from the page. The answer is actually a lot simpler than you might think: name everything. When you create your form, give every input field, bitmap, and button a unique name. This way, you can tell what button or bitmap the user hit, retrieve any text they entered, figure out which selections they made, or which radio or check boxes were selected. You have a fairly complete set of input field types to choose from, and fairly good control over the appearance of each.

Within a form, any button or bitmap selection will cause the transmission. The only way you can tell which button or bitmap was hit (and where) is to see if the name of the item was passed to your program. For this reason, use the NAME= parameter in the <INPUT TYPE> tag to create a unique name for the item. You can then use a query function (we'll get to that in section III, Basic CGI Operations) to see if the item was selected and, if so, what its value was.

Remember that the names are clear ASCII text and shouldn't contain any blanks. We also suggest that the names be meaningful, so you can figure them out next year when you go to revise your code.

Error Handling

You'll need to error check everything you read in. Data corrupted during transmission, the user not entering a critical piece of information, and a whole host of other problems can all cause your program to violate the cardinal rule of C -- Thou Shall Not Follow The Null Pointer. The code fragment we'll give you later in this series that reads in the page information and returns it to your application will return a NULL if the parameter you ask for wasn't present in the page transmission. You'll need to check this against NULL and generate an error page if there was some problem. Like all other pages, the error page needs to contain adequate recovery information so that you can get the user back to the page that caused the error after they've viewed the error. You'll also need to be careful about errors on your server. If, for example, your program requires access to a disk file, then you'll need to check the return from the open() statement to see if the file was in use by another copy of the program when you tried to open it. The actions you take on these errors can range from simply waiting until the resource becomes available, to generating an error page and sending it back to the user. Again, error detection and recovery should be considered carefully during the design phase.

Performance

Performance considerations should drive a lot of your design. You can't control the transport delay of the Internet, but you can minimize the impact on your users through careful design. The first and most obvious means is by limiting the number and size of the graphics you use in your program. We used a number of bitmaps in Stock market Challenge, but we made a significant effort to make sure that they were small and reusable whenever possible.

Another issue that can impact performance is the amount of data you ship across the Internet. If you use hidden fields, try to make sure that you're only sending down information that is pertinent to the page being displayed. Keep the names short but meaningful.

Finally, look at your server code itself and try to optimize it. Open files as read only, if you can, and close them as soon as you're done so that other copies of the program can get to them. Use optimized search and sort algorithms. Convert the strings that the HTTP server sends you to numbers, and operate on them that way internally. Many of us have gotten sloppy in the past few years as machines have gained power and speed. Pull out all your old ``good programming'' tools and use them again. Remember, the user is going to be waiting long enough for the transport delay. Don't make them wait longer for inefficient code.

John Fawcett and Bob Jepson are Senior Systems Programmers for Sierra On-Line, Inc., in Bellevue, Washington. They are the authors of Sierra's Web based Internet Stock Market Challenge game. Fawcett has been a programmer for over 15 years, with a background in Unix, data communications, and graphics. Jepson has been a programmer for over 20 years, with a background in business applications and DOS/Windows programming.