Although Java is being touted as the best way to do animation on the Web, you can also write CGI programs to produce animation. There are two mechanisms for creating animation: client pull and server push. In client pull, a new HTTP connection is opened every time a document is requested. In server push, however, the connection is kept open until all the data is received by the client. That is the main difference between the two mechanisms. As a result, you can have an animation in an otherwise static document by using the HTML <IMG> tag to access the CGI program instead of a URL to an image, as introduced in the "Inserting Multiple Dynamic Images" section at the beginning of this chapter.
Client pull requires a special directive either in the HTML document header or as a part of the CGI program's HTTP response. This directive instructs the client to retrieve a specified document after a certain amount of time. In other words, the client opens a new connection to the server for each updated image (see Figure 6.7).
Server push involves sending packets of data to the client periodically, as shown in Figure 6.8. The HTTP connection between the client and the server is kept open indefinitely. Server push can be implemented in a CGI program through the use of the multipart/x-mixed-replace MIME type.
Both client pull and server push are supported only by Netscape Navigator (version 1.1 and higher) and Internet Explorer.
Here is a simple example of an HTML document that displays the time continuously:
<META HTTP-EQUIV="Refresh" CONTENT=5> <!--#echo var="DATE_LOCAL"-->
Animation depends on updating the browser's window at regular intervals with new material from the server. Browsers provide a way to update their windows called refreshing. In the example shown above, we trick the browser into issuing its refresh command every five seconds, so that it retrieves the document. The document simply uses server side includes to display the current time. (See Chapter 5 for more information on Server Side Includes.)
The META tag is part of the HTML 3.0 specification used to simulate HTTP response headers in HTML documents. In this case, it is used to simulate the "Refresh:" HTTP header with a delay of five seconds.
The "Refresh:" header is non-repeating; it does not load the document repeatedly. However, in this example, "Refresh:" is specified on each retrieval, creating a continuous display.
Here is an example of a CGI program that performs the same operation as the previous HTML code:
#!/usr/local/bin/perl $delay = 5; $date = "/bin/date"; print "Refresh: ", $delay, "\n"; print "Content-type: text/plain", "\n\n"; print `$date`; exit(0);
Remember, SSI directives cannot be included in a CGI program. So, the date command is used to output the current date and time.
Now, let's look at the directive used to load a different document after a specified time:
<META HTTP-EQUIV="Refresh" CONTENT="5; URL=http://your.machine/name.html">
This example loads the file specified by the URL after five seconds. If the file name.html does not contain another "Refresh:" header, there is no animation, because "Refresh:" is non-repeating. The corresponding CGI statement would be:
print "Refresh: 5; URL=http://your.machine/name.html", "\n";
As a final example of client pull, here's a CGI program that loads a document with a random fortune message every ten seconds.
#!/usr/local/bin/perl $fortune = "/usr/local/bin/fortune"; $refresh_time = 10; print "Refresh: ", $refresh_time, "\n"; print "Content-type: text/plain", "\n\n"; print "Here is another fortune...", "\n"; print `$fortune`; exit(0);
This is a repeating document, because a "Refresh:" header is specified every time the program is executed. The program uses the UNIX fortune command, which generates a random fortune each time it is invoked.
Server push animations can be created using the multipart/x-mixed-replace MIME type. The "replace" indicates that each data packet replaces the previous data packet. As a result, you can make smooth animations. Here is the format in which this MIME type is used:
Content-type: multipart/x-mixed-replace;boundary=End --End Content-type: image/gif Image #1 --End Content-type: image/gif Image #2 --End Content-type: image/gif Image #3 --End--
In the first Content-type declaration, we declare the multipart/x-mixed-replace content types and establish "End" as the boundary string. We then repeatedly display new images (declaring new content types of image/gif), ending each image with the "--End" string. The result is that the images are displayed one after another.
Let's look at an example that uses the server push mechanism.
#!/usr/local/bin/perl $| = 1; $webmaster = "shishir\@bu\.edu"; $boundary_string = "\n" . "--End" . "\n"; $end_of_data = "\n" . "--End--" . "\n"; $delay_time = 1;
First, we define the boundary strings that need to be sent to the client. We also set the delay time between images-- in this case, one second.
@image_list = ( "image_1.gif", "image_2.gif", "image_3.gif", "image_4.gif", "image_5.gif" );
All of the images that will be used in the animation are stored in the @image_list array. In this simple example, we use only 5 images.
$browser = $ENV{'HTTP_USER_AGENT'}; if ($browser =~ m#^Mozilla/(1\.[^0]|[2-9])#) { print "Content-type: multipart/x-mixed-replace;boundary=End", "\n";
The name of the client browser is obtained using the environment variable HTTP_USER_AGENT. If the browser is Netscape version 1.1 or higher, the multipart MIME type is sent to it, along with the initial boundary string. (Netscape uses "Mozilla" as its user agent string.)
for ($loop=0; $loop < scalar (@image_list); $loop++) { &open_and_display_GIF ($image_list[$loop]); print $boundary_string; sleep ($delay_time); } print $end_of_data;
A loop is used to iterate through the image_list array. Each image is displayed using the open_and_display_GIF subroutine. A boundary is then sent to the client, and the program proceeds to sleep for the specified amount of time. It is important to print the boundary after the image and before the sleep command to ensure that the server "pushes" the entire image to the client. The process is repeated for all the images in the array. Finally, the terminating boundary string is sent to the client.
} else { &open_and_display_GIF ($image_list[0]); } exit(0);
If the browser is not Netscape version 1.1 or higher, only the first image stored in the array is displayed.
sub open_and_display_GIF { local ($file) = @_; local ($content_length); if ( (open (FILE, "<" . $file)) ) { $content_length = (stat (FILE))[7]; print "Content-type: image/gif", "\n"; print "Content-length: ", $content_length, "\n\n"; print <FILE>; close (FILE); } else { &return_error (500, "File Access Error", "Cannot open graphic file $file!"); } }
This routine should be very familiar to you. First, it sends the image/gif MIME type, along with the length of the image. Then, the image is printed to standard output.
One final note: If you are using an NCSA server, it is better to create the CGI server push animation program as a non-parsed header ("nph") script, as described in Chapter 3, Output from the Common Gateway Interface. That way the server will not parse the HTTP headers, and instead will send the information directly to the client. The main advantage of this is reduced "jerkiness" in the animation. Just to refresh your memory, you need to name the script with an "nph-" prefix, and the first lines that are output from your script should be:
print "HTTP/1.0 200 OK", "\n"; print "Content-type: multipart/x-mixed-replace;boundary=End", "\n";