Client pull
Server push
Communication between a user's Web browser and your Web server always begins from the browser side. The user either enters the URL or clicks on a link containing a URL to a document on your Web server. The Web browser contacts your Web server and opens an HTTP communications channel between the two. The Web server takes the request received from the Web browser, processes it, and returns the results (usually the document requested) through the open communications channel. Having sent the results of the request, the Web server shuts down the HTTP communications channel, severing the connection between itself and the user's Web browser.This method of communication requires you to wait for the user to request information on your Web server before sending that information. However, under some circumstances you may want to send the data before the user requests it. For example, if you wanted to display your company's current stock price on your Web page, you could easily generate the page from a CGI script that has access to the current stock price. But once the page is sent to the user's Web browser, you can no longer update the stock price until the user reloads the Web page. So, even though you generated the HTML page with a CGI script, the page will contain outdated data within minutes. Netscape has devised a solution to this problem by developing two methods of updating information in the user's Web browser without any action by the user. These methods, known as client pull and server push, are the topic of this chapter.
Note: Client pull and server push are implemented by Netscape through changes to their Navigator browser software, and are supported by all versions of Netscape Navigator starting with 1.1. However, most other browsers do not yet support either of these methods.Client pull
Client pull is the Web browser requesting, or pulling, a Web document from a Web server without the user entering a URL, clicking on a link, or pressing the reload button. From this definition, you might imagine that your Web browser can wildly start loading any document it chooses. But this is not the case. Client pull only occurs when there is a special directive in a document you told your Web browser to request from a Web server. This special directive is a simple HTML 3.0 tag, <META>, that is used to simulate HTTP response headers. In other words, directives in the <META> tag are included with the HTTP response headers sent from the Web server. For example, when the tag
<META HTTP-EQUIV="Refresh" CONTENT=5>
is included in an HTML document, a Web browser that supports the <META> tag will include the header
Refresh: 5
with the HTTP response headers that were sent from the Web server.You can use the <META> tag and Refresh HTTP response header together to cause a user's Web browser to reload the current page or load a different Web document after a specified amount of time. So, a document containing the preceding <META> tag would reload itself after 5 seconds had elapsed. This reload will continue to occur as long as the Web browser is displaying an HTML page with the preceding <META> tag.
As you can see, client pull does not require any CGI scripting to implement. It is defined through the use of the <META> tag and the Refresh HTTP response header. However, you can use CGI scripting along with client pull to extend what you can do with client pull alone. For example, with a static HTML page containing a <META> tag that reloads the page every five seconds--as shown in Listing 8.1 and Figure 8.1--the user would just see the same page being reloaded endlessly, without seeing any new information. If the same page contained a Server Side Include that called a CGI script that output the current date and time from the server, the user would see new information every time the page reloaded. Listing 8.2 shows the HTML code with a Server Side Include and Figure 8.2 shows what the page would look like during one of the reloads.
Listing 8.1: HTML Code for a Static HTML Client Pull <HTML> <HEAD> <META HTTP-EQUIV="Refresh" CONTENT=5> <TITLE>Static HTML Client Pull</TITLE> </HEAD> <BODY> <H1>This Page Reloads Itself</H1> This page will automatically reload every 5 seconds. Every time it reloads, it will look exactly the same. </BODY> </HTML>
Figure 8.1: The static HTML client pull
Listing 8.2: HTML Code for a More Dynamic Client Pull <HTML> <HEAD> <META HTTP-EQUIV="Refresh" CONTENT=5> <TITLE>More Dynamic Client Pull</TITLE> </HEAD> <BODY> <H1>This Page Reloads Itself</H1> This page will automatically reload every 5 seconds. Every time it reloads, the current date and time are displayed below. <P> <!--#exec cgi="/cgi-bin/date.pl" --> </BODY> </HTML>
Figure 8.2: The more dynamic client pull The following sections include some examples of client pull. The first example demonstrates how to use a splash screen and uses no CGI script. Because the splash screen requires no CGI scripting, you do it entirely in HTML. However, this example contains graphic images that you will not have on your own Webserver. For this reason, you're supplied with the URL so you can see for yourself how the HTML code works. Even though client pull is implemented to HTML, which limits you to static HTML pages, you'll see that there are still some applications for client pull alone. The second example creates a guided tour of the Educational Software Web site and demonstrates how a CGI script can further enhance the client pull feature.
A Splash Screen
Creating a splash screen for the first page of your Web site is a creative way to use client pull without any CGI scripting. A splash screen is an identifying image that appears when you start a program. Most commercial programs feature splash screens. For example, Netscape Navigator 2.0 for Windows displays the image shown in Figure 8.3 every time you start the program. Using Client pull, you can create a splash screen for your Web site.
Figure 8.3: The Netscape Navigator splash screen For this example, you will create the HTML code for a splash screen for the Actors Album Web site. Because you implement this example by adding a single line of HTML code to a normal HTML page, you'll see how to create a splash screen for your own Web site. Start by seeing how the Actors Album splash screen works. You can do this by visiting the URL http://www. castingguild.com/actors-album/splash.html using the Netscape Navigator Web browser (a version newer than 1.1). (If you don't have Netscape, you can download it and try it for free from Netscape's Web site, http://www. netscape.com.) Looking at the Actors Album splash screen will give you a better idea of how the splash screen works. Figures 8.4 and 8.5 show the two screens you will see. First the splash screen shown in Figure 8.4 loads into your Netscape browser. Five seconds later, the Netscape browser automatically sends a request for the home page shown in Figure 8.5. Even though the browser sends the request after five seconds, how soon the page is displayed depends on the speed of your Internet connection. Listing 8.3 includes the HTML code for the Actors Album splash screen.
Figure 8.4: Actors Album splash screen
Figure 8.5: Actors Album home page
Listing 8.3: The HTML for the Actors Album Splash Screen <HTML> <HEAD> <META HTTP-EQUIV="Refresh" CONTENT="5; URL=http://www.castingguild.com/actors-album/"> <TITLE>Actors Album</TITLE> </HEAD> <BODY BGCOLOR="#FFFFFF"> <CENTER> <IMG HEIGHT=233 WIDTH=3Ø5 VSPACE=1Ø HSPACE=1Ø SRC="graphics/splash.gif"> </CENTER> </BODY> </HTML>
Apart from the <META> tag, this HTML code is similar to the code at numerous other Web sites. As you learned earlier in this chapter, the <META> tag drives the client pull action. This version of the <META> tag is slightly different from the ones you saw earlier. In the attribute CONTENT is the URL for another document. By specifying the URL in this manner, you can instruct the Web browser to load a different document instead of reloading the same one. The line
<META HTTP-EQUIV="Refresh" CONTENT="5; URL=http://www.castingguild.com/actors-album/">
tells a client pull enabled Web browser to load the URL http://www.castingguild. com/actors-album/ after five seconds. Remember that when the URL does not specify a document name (as here), the Web server returns the index.html file if one exists. The Actors Album's index.html file contains no <META> tag, so no further client pulls are initiated. In other words, client pull briefly displays the first page, splash.html, which is followed immediately by the Web site's home page.You can easily implement a splash screen for your own Web site. Simply create an HTML file that will be the splash screen, and add the <META> tag to the HTML code of the splash screen file. The code for your <META> tag would look like
<META HTTP-EQUIV="Refresh" CONTENT="5; URL=Your home page URL">
with your home page's URL specified. Remember that your splash screen will only work with Web browsers that support client pull. So, if your splash screen doesn't contain any links to your real home page, as is the case in the Actors Album splash screen, don't make it the default page for your Web site. Doing so would strand users on your splash screen page if their Web browser did not support client pull. They would have no alternate way to get to your home page.
A Guided Tour
Now let's take the client pull feature and extend it by using a CGI script. For this example you will create a guided tour of the Educational Software Web site. This Web site has only three pages--the home page, the Software Downloads page, and the Technical Support page. The guided tour feature will take the user through each page of the Educational Software Web site, explaining what information is on each Web page and what the user can do on the page. Although you can implement a guided tour without client pull, you will use client pull so the user does not have to click on links to go to the next page in the tour. In the next sections you will create the Web pages for the Educational Software Web site and write a CGI script that takes the user through each Web page.
The Educational Software Web Pages
To set up the guided tour for the Educational Software Web site, you first need to create the home page, Software Downloads page, and Technical Support page of the Web site. The home page will contain some information about Educational Software and some announcements about software that is coming soon or is newly available. Listing 8.4 contains the HTML code for the Educational Software home page and Figure 8.6 shows the page in the Netscape browser. Notice there is already a link for the guided tour that calls the CGI script guided.pl. This is the guided tour CGI script you will be developing later in this section.
Listing 8.4: HTML Code for the Educational Software Home Page <HTML> <HEAD> <TITLE>Educational Software</TITLE> </HEAD> <BODY> <H1>Educational Software</H1> Educational Software provides freeware software with an educational purpose. Currently, Educational Software only has two software titles, Algebra Primer and Vocabulary Builder, which are both available from the <A HREF="downloads.html">software downloads page</A>. We are now working on a third title, Elementary Physics, which should be available in August. <P> If you have Netscape Navigator 1.1 or greater, you can select the <A HREF="/cgi-bin/guided.pl">Guided Tour</A>, which will acquaint you with our Web site. <P> [ <A HREF="downloads.html">Software Downloads</A> | <A HREF="tech- support.html">Technical Support</A> | <A HREF="/cgi- bin/guided.pl">Guided Tour</A> ] </BODY> </HTML>
Figure 8.6: Educational Software's home page
Listing 8.5: HTML Code for the Software Downloads Page <HTML> <HEAD> <TITLE>Educational Software - Software Downloads</TITLE> </HEAD> <BODY> <H1>Educational Software - Software Downloads</H1> <TABLE> <TR> <TH ALIGN=left>Macintosh</TH> <TH WIDTH=5Ø></TH> <TH ALIGN=left>Windows</TH> </TR> <TR> <TD ALIGN=left><A HREF="algebra.hqx">Algebra Primer</A></TD> <TD WIDTH=5Ø></TD> <TD ALIGN=left><A HREF="algebra.zip">Algebra Primer</A></TD> </TR> <TR> <TD ALIGN=left><A HREF="vocab.hqx">Vocabulary Builder</A></TD> <TD WIDTH=5Ø></TD> <TD ALIGN=left><A HREF="vocab.zip">Vocabulary Builder</A></TD> </TR> </TABLE> <P> [ <A HREF="home.html">Home</A> | <A HREF="tech-support.html">Technical Support</A> ] </BODY> </HTML>
Figure 8.7: The Software Downloads page Educational Software's Software Downloads page will contain links to all the software programs that are available for downloading. The software is listed as freeware--that is, it doesn't cost anything. For this example, the Software Downloads page contains two programs, Algebra Primer and Vocabulary Builder. There are Macintosh and Windows versions of the programs, which the user can download simply by clicking on the appropriate link. Listing 8.5 contains the HTML code for the Software Downloads page and Figure 8.7 shows how the page will appear in the Netscape browser.
The Technical Support page lets users of Educational Software programs send messages to Educational Software when they have technical problems with the software. The Technical Support page contains the same elements as the feedback form in Chapter 4. Listing 8.6 contains the HTML code for the Technical Support Web page, which is displayed in Figure 8.8.
Listing 8.6: HTML Code for the Technical Support Page <HTML> <HEAD> <TITLE>Educational Software - Technical Support Form</TITLE> </HEAD> <BODY> <H1>Educational Software - Technical Support Form</H1> <FORM METHOD=POST ACTION="/cgi-bin/feedback.pl"> <B>Name</B><BR><INPUT NAME="name" SIZE=42> <P><B>E-mail Address</B><BR><INPUT NAME="email" SIZE=42> <P><B>Comments</B><BR><TEXTAREA NAME="comments" ROWS=1Ø COLS=38></TEXTAREA> <P><INPUT TYPE="submit" VALUE="Send"></FORM> <P> [ <A HREF="home.html">Home</A> | <A HREF="downloads.html">Software Downloads</A> ] </BODY> </HTML>
The Guided Tour Script
Now that you have the Web pages for the Educational Software Web site, you can write the guided tour CGI script that takes users through each Web page of the site. The guided tour begins when the user selects the guided tour link at the bottom of the home page, which calls the guided.pl script. The guided.pl script opens the HTML file for each Web page, adding some new lines at the beginning (including a line with the <META> tag to do the client pull) and then returns the modified Web page to the user's browser. After 30 seconds, the user's Web browser sends a new request to the guided.pl script, which then sends back a modified version of the next Web page. This process continues until the guided.pl script has sent modified versions of all the Web pages from the Web site. The guided tour ends by returning the user to the Educational Software home page.
Figure 8.8: The Tchnical Support page When the user selects the guided tour link on the home page, the guided tour script starts. The script reads in the HTML code for the home page from the home.html file and modifies it to contain a brief explanation of the Web page. Also, the script adds the <META> tag in the header portion of the modified home page. Because you are using client pull for the guided tour, you need to add this line so the next page loads automatically. Because the guided tour script needs to modify the Web page before displaying it to the user, the <META> tag must request the guided.pl script file. The basic form of this <META> tag is
<META HTTP-EQUIV="Refresh" CONTENT="3Ø; URL=http://www.robertm.com/cgi- bin/guided.pl?page=pagename.html">
Notice that the guided tour script receives the parameter page=pagename.html when it is called. This lets the guided tour script know which Web page to return to the user's Web browser.The guided tour script adds the preceding <META> tag to the modified versions of the Web pages. For the script to work properly, it needs to know which Web page is to be displayed next, so that it can insert the page name in the <META> tag. For example, if the current page to be displayed were the Software Downloads page, the guided tour script would need to insert the following <META> tag at the beginning of the downloads.html file:
<META HTTP-EQUIV="Refresh" CONTENT="3Ø; URL=http://www.robertm.com/cgi- bin/guided.pl?page=tech-support.html">
Notice that pagename.html has been changed to tech-support.html. This is because the Technical Support page is the page displayed after the Software Downloads page. To know which Web page is next, your guided tour script needs to keep track of the order in which the Web pages are to be displayed. You can do this by storing the name of all the Web pages from the site in an array, like this:
@pages = ("home.html", "downloads.html", "tech-support.html");
The script will display a modified version of each page in the array, in the order in which they appear in the array. So, for the @pages array, the guided tour script will first display a modified version of the home page, followed by modified versions of the Software Downloads page and the Technical Support page.Now that you have the order of the Web pages stored in the @pages array, you can write the Perl code to have your guided tour script formulate the <META> tag for the page being modified:
shift(@pages) until $pages[0] eq $data{'page'}; if (@pages > 1) { $url_prefix .= $pages[1]; } else { $url_prefix = $homepage; } $add_tag = "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"3Ø; URL=$url_prefix\">\n";
The first line contains two parts, the shift expression, shift(@pages), and the until loop. The shift expression removes the first item from the @pages array. The until loop causes the shift statement to repeatedly execute until the first element in the @pages array is equal to the value in the $data{'page'} element. As before, the %data array is the associative array that stores the data your CGI script receives when called. For this example, the only data being passed to the CGI script is the name of the Web page to be displayed. Because you move the name of the current page to the first element of the @pages array, your script can always access the next element by referencing the second element in the @pages array. For example, if $data{'page'} were downloads.html, the first element of the @pages array, home.html, would be removed. The array would then contain only two elements, downloads.html and tech-support.html. The first element, $pages[0], would be the current page to display, downloads.html. The second element, $pages[1], would be the next page to display, tech-support. html, and would be the value to insert into the <META> tag of the downloads. html page.The name of the next Web page to be displayed is appended to the URL prefix in the preceding if...else statement. Notice that the if statement compares the array @pages to the number 1. When the @pages array is used in this context, it is referencing the length of the array, not any individual array element. So, the if statement checks whether the array contains more than one element. If it does--as in the previous example with the downloads.html page--the next page, or $pages[1], is appended to the $url_prefix variable, which is set earlier in the code with the line
$url_prefix = "http://www.thepalace.com/cgi-bin/guided.pl?page=";
Listing 8.8, which appears later, shows exactly where the previous line is placed. If the @pages array only has one element, which would be the case when $data{'page'} is set to tech-support.html, the guided tour script is generating the last page to be displayed in the tour. When the guided tour is finished, the user is returned to the Educational Software home page. When the @pages array only contains the one element, the <META> tag needs to look like
<META HTTP-EQUIV="Refresh" CONTENT="3Ø; URL=http://www.robertm.com/home.html">
So, the value of the $url_prefix variable is changed to the value of the $home-page variable, which is also set elsewhere in the guided tour script with the line
$homepage = "http://www.robertm.com/home.html";
After the if...else statement block, the $url_prefix variable will contain the correct path to be added in the <META> tag for the current page. The line
$add_tag = "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"3Ø; URL=$url_prefix\">\n";
creates the string for the <META> tag and stores it in the $add_tag variable for later inclusion in the HTML code that the guided tour script will send back to the user's Web browser.With the <META> tag properly formatted for the current Web page to be displayed in the tour, the script is ready to modify the contents of the existing Web page. First you will create a variable $guide, which stores the HTML code for the text to be included at the top of the page. This text is a simple explanation of the Web page the user is currently viewing in the tour. For example, with the home page, the text could be
This is the Educational Software home page. It includes announcements about new and upcoming software releases.
An easy way to include the correct explanatory text for the page to be displayed is to store the explanations in an associative array keyed by the names of the Web pages. The following lines do this:
%guide_text = ( "home.html", "This is the Educational Software home page. " . "It includes announcements about new and upcoming " . "software releases.", "downloads.html", "From this page, you can download the freeware " . "software. There are versions available for both " . "Macintosh and Windows machines. To download, just " . "click on the software title under your machine's type.", "tech-support.html", "Use this feedback form to send us messages when " . "you have technical problems with our software.", );
Then, to format the explanatory text with HTML tags for inclusion in the modified version of the Web page, you would write
$guide = "<CENTER><HR>$guide_text{$data{\"page\"}}<HR></CENTER>";
Because the $data{'page'} element holds the name of the current page, using this for the key of the %guide_text array accesses the correct explanation for the current page.Next you need to open the HTML file of the current page to be modified and displayed in the tour. You do this with the following three lines, which open the HTML file, read all of the contents into the @template array (one line per element in the array), and close the HTML file.
open(TEMPLATE, "$path/$data{\"page\"}") || die "Content-type: text/html\n\nCannot open HTML files!"; @template = <TEMPLATE>; close(TEMPLATE);
The first line opens the HTML file for input. The path and file name of the HTML file are stored in the $path and $data{'page'} variables, which are set at the beginning of the guided tour script. The die statement terminates the program and outputs the contents of the string.The || operator between the open and die statements is the logical or operator. If you place this operator between the two statements, the Perl interpreter first tries to execute the open statement. If the open is successful, the Perl interpreter moves on to the next line of code. However, if the file cannot be opened, the Perl interpreter executes the die statement. This is a common way to verify whether a file has opened successfully and to terminate the Perl program if it has not.
The second line in the preceding Perl code reads in the contents of the HTML file from the input stream <TEMPLATE> and places each line in an element of the array @template. After the contents of the HTML file have been read into the @template array, you can close the input stream <TEMPLATE> with the close command, as in the third line of code.
Finally, you need to modify the lines of the @template array that will be different for the guided tour. First you will modify the line containing the <TITLE> tags. In Listings 8.4, 8.5, and 8.6, the <TITLE> tags are all on the third line of the HTML file. Because the indexing of Perl arrays begins with 0, you can change the third element by using the following line:
$template[2] = "<TITLE>Guided Tour - $page_names{$data{\"page\"}}</TITLE>\n";
This line changes the title to Guided Tour - pagename, where pagename is the name of the page currently being displayed. Because you want to display the name of the page and not the name of the HTML file, you can store the names of the pages in the %page_names associative array, which is keyed by the names of the HTML files. This is done in the following lines:
%page_names = ( "home.html", "Home Page", "downloads.html", "Software Downloads Page", "tech-support.html", "Technical Support Page", );
When the $data{'page'} variable is set to downloads.html, the title line is changed to
<TITLE>Guided Tour - Software Downloads Page</TITLE>
After modifying the title line, you need to add the lines containing the <META> tag and the explanation text. You do this with the following two lines of code:
splice(@template, 5, Ø, $guide); splice(@template, 2, Ø, $add_tag);
Both lines use the splice operator, which inserts the contents of the variables $guide and $add_tag into the @template array. In the first line, the contents of the $guide variable are inserted into the @template array after the fifth element in the array. This places the explanatory text right after the <BODY> tag for all the Web pages, as shown in Listings 8.4, 8.5, and 8.6. The second line inserts the contents of the $add_tag variable into the @template array after the second element in the array. For all the Web pages, this would place the <META> tag immediately after the <HEAD> tag, as shown in Listings 8.4, 8.5, and 8.6. The 0 used in both lines of code indicates to Perl to insert the contents of the variables without removing any of the current elements of the array.Finally, with the @template array containing the modified version of the HTML page to display to the user, you need to output the parsed header and the contents of the @template array. You do this with the following two lines of Perl:
print "Content-type: text/html\n\n"; print @template;
Listing 8.7 contains the Perl code that you just finished developing. For readability, it has been placed into a subroutine called Display. The only additions are the sub Display line, which declares the name of the subroutine, and the local variable declarations in the first two lines to the subroutine. The local statements
local (%data) = @_; local (@template, $guide, $add_tag);
declare the arrays and variables as local to the Display subroutine. As you may remember, a local variable is a variable that exists only within a portion of your Perl code, usually within a subroutine. If a variable with the same name existed outside the subroutine, Perl would consider it a different variable than the one that is declared local within the subroutine. Declaring your subroutine's variables as local helps to keep your subroutines from overwriting values of global variables. A global variable is one that is accessible throughout the entire Perl program, including any subroutines in the same Perl file. In Listing 8.7, the variables $url_prefix and $path and the arrays @pages, %guide_text, and %page_names are global variables.
Listing 8.7: The Display Subroutine sub Display { local (%data) = @_; local (@template, $guide, $add_tag); $data{'page'} = "home.html" unless $data{'page'}; shift(@pages) until $pages[Ø] eq $data{'page'}; if (@pages > 1) { $url_prefix .= $pages[1]; } else { $url_prefix = $homepage; } $add_tag = "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"3Ø; URL=$url_prefix\">\n"; $guide = "<CENTER><HR>$guide_text{$data{\"page\"}}<HR></CENTER>"; open(TEMPLATE, "$path/$data{\"page\"}") || die "Content-type: text/html\n\nCannot open HTML files!"; @template = <TEMPLATE>; close(TEMPLATE); $template[2] = "<TITLE>Guided Tour - $page_names{$data{\"page\"}}</TITLE>\n"; splice(@template, 5, Ø, $guide); splice(@template, 2, Ø, $add_tag); print "Content-type: text/html\n\n"; print @template; }
To finish the guided tour script, you just need to place the code for the Display subroutine, listed in Listing 8.7, into the guided.pl file along with the declarations of global variables and the code for the No_SSI and User_Data subroutines you have been using throughout this book. Listing 8.8 contains the entire code for the guided tour script. Be sure to change the $path, $url_prefix, and $homepage variables to contain the correct paths and URLs for your machine. Also, Windows users should remove the first line of the script, which is specific to UNIX systems. Figures 8.9, 8.10, and 8.11 display how the modified home page, Software Downloads page, and Technical Support pages look in the guided tour.
Listing 8.8: The guided.pl File #!/usr/local/bin/perl # All users need to change the paths and URLs to be correct for # their machines. Windows users need to have the $path variable # in the form $path = "c:\\robertm\\guided" $path = "/users/robertm/guided"; $url_prefix = "http://www.robertm.com/cgi-bin/guided.pl?page="; $homepage = "http://www.robertm.com/home.html"; @pages = ("home.html", "downloads.html", "tech-support.html"); %page_names = ( "home.html", "Home Page", "downloads.html", "Software Downloads Page", "tech-support.html", "Technical Support Page", ); %guide_text = ( "home.html", "This is the Educational Software home page. " . "It includes announcements about new and upcoming " . "software releases.", "downloads.html", "From this page, you can download the freeware " . "software. There are versions available for both " . "Macintosh and Windows machines. To download, just " . "click on the software title under your machine's type.", "tech-support.html", "Use this feedback form to send us messages when " . "you have technical problems with our software.", ); if ($ENV{'REQUEST_METHOD'} eq "GET") { %data_received = &User_Data; &No_SSI(*data_received); &Display(%data_received); } else { print "Content-type: text/html\n\nYou are not using this script correctly!"; } sub Display { local (%data) = @_; local (@template, $guide); $data{'page'} = "home.html" unless $data{'page'}; shift(@pages) until $pages[Ø] eq $data{'page'}; if (@pages > 1) { $url_prefix .= $pages[1]; } else { $url_prefix = $homepage; } $add_tag = "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"3Ø; URL=$url_prefix\">\n"; $guide = "<CENTER><HR>$guide_text{$data{\"page\"}}<HR></CENTER>"; # Windows users need to change the string "$path/$data{\"page\"}" to # "$path\\$data{\"page\"}" open(TEMPLATE, "$path/$data{\"page\"}") || die "Content-type: text/html\n\nCannot open HTML files!"; @template = <TEMPLATE>; close(TEMPLATE); $template[2] = "<TITLE>Guided Tour - $page_names{$data{\"page\"}}</TITLE>\n"; splice(@template, 5, Ø, $guide); splice(@template, 2, Ø, $add_tag); print "Content-type: text/html\n\n"; print @template; } sub No_SSI { local (*data) = @_; foreach $key (sort keys(%data)) { $data{$key} =~ s/<!--(.|\n)*-->//g; } } sub User_Data { local (%user_data, $user_string, $name_value_pair, @name_value_pairs, $name, $value); # If the data was sent via POST, then it is available # from standard input. Otherwise, the data is in the # QUERY_STRING environment variable. if ($ENV{'REQUEST_METHOD'} eq "POST") { read(STDIN,$user_string,$ENV{'CONTENT_LENGTH'}); } else { $user_string = $ENV{'QUERY_STRING'}; } # This line changes the + signs to spaces. $user_string =~ s/\+/ /g; # This line places each name/value pair as a separate # element in the name_value_pairs array. @name_value_pairs = split(/&/, $user_string); # This code loops over each element in the name_value_pairs # array, splits it on the = sign, and places the value # into the user_data associative array with the name as the # key. foreach $name_value_pair (@name_value_pairs) { ($name, $value) = split(/=/, $name_value_pair); # These two lines decode the values from any URL # hexadecimal encoding. The first section searches for a # hexadecimal number and the second part converts the # hex number to decimal and returns the character # equivalent. $name =~ s/%([a-fA-FØ-9][a-fA-FØ-9])/pack("C",hex($1))/ge; $value =~ s/%([a-fA-FØ-9][a-fA-FØ-9])/pack("C",hex($1))/ge; # If the name/value pair has already been given a value, # as in the case of multiple items being selected, then # separate the items with a " : ". if (defined($user_data{$name})) { $user_data{$name} .= " : " . $value; } else { $user_data{$name} = $value; } } return %user_data; }
Server push
When you use client pull, every refresh action taken by the Web browser opens a new HTTP communication channel. Recall that client pull starts when the Web browser requests a document containing the <META> tag, which instructs the Web browser to refresh the document after a specified amount of time. The Web server always shuts down the HTTP communication channel after the document has been sent. When the Web browser performs the refresh, it sends a new HTTP request to the Web server for the same document (or for the new document specified by the inclusion of a redirect URL, as demonstrated in the splash screen example earlier in this chapter). Once again, the Web browser opens communication with the Web server, which returns the document and closes the communication channel.Unlike client pull, server push keeps the HTTP communication channel open between the Web browser and the Web server. Then, when instructed to, the Web server sends data to the Web browser through this open communications channel. By keeping the channel open, the Web server has complete control of when data is sent to the Web browser. The Web server does not have to wait for the Web browser to initiate the communication.
Server push is initiated through a CGI script that holds the communication channel between the Web browser and the Web server open until the script finishes executing. You do this by using a variation on the multipart/mixed MIME type. Recall from Chapter 2 that the parsed header Content-type takes MIME types for its values. In previous examples, you have used the text/html and image/gif MIME types. For most HTTP responses, there is only one block of data sent to the Web browser, which only needs one MIME type for the data being returned to the Web browser. However, the multipart/mixed MIME type allows for multiple blocks of data to be sent in the same HTTP response.
Figure 8.9: The Educational Software Home page in the guided tour The multipart/mixed MIME type uses a boundary string to separate the blocks of data being returned to the Web browser. Each data block has its own MIME type specified at the beginning of the block. You can use any string for the boundary separator. However, it's important to use a string that does not occur in the data being returned. A typical multipart/mixed parsed header looks like
Content-type: multipart/mixed;boundary=ThisRandomString
where ThisRandomString is the string boundary. As with other parsed headers, the multipart/mixed must be followed immediately by a blank line. Then at the beginning of each data block, you place the boundary string preceded by two dashes, followed on the next line by the MIME type for that block of data. You must have a blank line between the MIME type and the data being returned. You must also have a blank line immediately after the data object. So, the header and data for each block would look like
--ThisRandomString Content-type: text/html Data for the first object.
Figure 8.10: The Software Downloads page in the guided tour After you have included all the blocks of data you want returned to the user's Web browser, simply output the end of boundary string, which is the boundary string preceded and followed by two dashes, as in
--ThisRandomString--
Here is an example of an HTTP response using the multipart/mixed MIME type:
Content-type: multipart/mixed;boundary=ThisRandomString --ThisRandomString Content-type: text/html Data for the first object. --ThisRandomString Content-type: text/html Data for the second and last object. --ThisRandomString--
Figure 8.11: the Technical Support page in the guided tour This example contains two HTML objects that are returned to the user in the HTTP response.
Server push uses a variant of the multipart/mixed MIME type, the multipart/x-mixed-replace MIME type. This is an experimental MIME type created by Netscape for server push. The difference between this MIME type and the standard multipart/mixed MIME type is that multipart/x-mixed-replace replaces previous data blocks with the subsequent data blocks. The format for the multipart/x-mixed-replace MIME type is identical to the above multipart/mixed example. Simply change the first line to
Content-type: multipart/x-mixed-replace;boundary=ThisRandomString
The major drawback with server push is that the HTTP communications channel remains open until the Web server is finished sending all the data. This means that your Web server must dedicate a TCP/IP port to the user's Web browser for the duration of the server push. When you use server push, you can keep this channel open indefinitely, sending data down whenever you want. Because all systems have a limited number of TCP/IP ports, this might overload your Web server. For this reason, you should use server push with discretion, especially if your Web server machine has limited TCP/IP ports.
The Pushed Guided Tour
Using server push, you can redo the guided tour script to push down the modified pages instead of having the Web browser pull down the pages. This makes your guided tour script much simpler, because it does not have to receive any input about the current page to display. The script is only executed once for the entire guided tour instead of once for each Web page as with the client pull guided tour. The script just has to loop over the @pages array and send down the proper header and modified Web page for each element in the @pages array. Listing 8.9 contains the Perl code for the server push guided tour. To run this guided tour, place the code in a file called push-guided.pl (or p-guide.pl if you are restricted to an eight-character file name) and use the same three HTML pages from the client pull guided tour, as shown in Listings 8.4, 8.5, and 8.6. All users need to make the specified change to the path variable. Also, Windows users need to remove the first line and change the file names in both open statements, as noted in the comments.
Listing 8.9: The push-guided.pl File #!/usr/local/bin/perl # All users need to change the path to a valid path for # their machine. Windows users need a path in the form # $path="c:\\robertm\\guided"; $path = "/users/robertm/guided"; @pages = ("home.html", "downloads.html", "tech-support.html"); %page_names = ( "home.html", "Home Page", "downloads.html", "Software Downloads Page", "tech-support.html", "Technical Support Page", ); %guide_text = ( "home.html", "This is the Educational Software home page. " . "It includes announcements about new and upcoming " . "software releases.", "downloads.html", "From this page, you can download the freeware " . "software. There are versions available for both " . "Macintosh and windows machines. To download, just " . "click on the software title under your machine's type.", "tech-support.html", "Use this feedback form to send us messages when " . "you have technical problems with our software.", ); $|=1; print "Content-type: multipart/x-mixed-replace;boundary=ThisRandomString\n\n"; foreach (@pages) { print "--ThisRandomString\n"; print "Content-type: text/html\n\n"; $guide = "<CENTER><HR>$guide_text{$_}<HR></CENTER>"; # Windows users need to change the string "$path/$_" to # "$path\\$_" open(TEMPLATE, "$path/$_") || die "Content-type: text/html\n\nCannot open HTML files!"; @template = <TEMPLATE>; close(TEMPLATE); $template[2] = "<TITLE>Guided Tour - $page_names{$_}</TITLE>\n"; splice(@template, 5, Ø, $guide); print @template; print "\n\n"; sleep 3Ø; } print "--ThisRandomString\n"; print "Content-type: text/html\n\n"; # Windows users need to change the string "$path/home.html" to # "$path\\home.html" open(TEMPLATE, "$path/home.html") || die "Content-type: text/html\n\nCannot open HTML files!"; @template = <TEMPLATE>; close(TEMPLATE); print @template; print "\n\n"; print "--ThisRandomString--\n\n";
The first thing you should notice in Listing 8.9 is the line
$|=1;
This is a special Perl variable that, when set to a nonzero value, forces a flush of the output stream (standard output in this example) after every print statement. In other words, it keeps the output stream from being buffered. If you don't include this line in all your server push scripts written in Perl, the output will be buffered and sent to the browser all at once. For this example, buffering the output would result in all four HTML pages (the three modified for the guided tour, and the home page) being sent to the browser after 90 seconds. To the user, it would appear as though only the home page was reloaded.The next lines in Listing 8.9 output the multipart/x-mixed-replace parsed header to the Web browser and start the foreach loop, which loops over each element in the @pages array. The body of the foreach loop is executed once for each element in the @pages array. First, the body of the foreach loop prints the parsed header for that block of data. The next lines should look familiar from the client pull guided tour example. The explanatory text is placed in the $guide variable, the file is opened and read into the @template array, the file is closed, the contents of the @template array are modified, and the modified contents are then returned to the user's Web browser. Notice how the variable $_ is used in the following lines from Listing 8.9:
$guide = "<CENTER><HR>$guide_text{$_}<HR></CENTER>"; open(TEMPLATE, "$path/$_") || die "Content-type: text/html\n\nCannot open HTML files!"; $template[2] = "<TITLE>Guided Tour - $page_names{$_}</TITLE>\n";
The $_ variable is a special Perl variable that in this context takes on the value of the current array element from the @pages array. For example, in the first iteration of the foreach loop, the $_ variable is equal to home.html. In the second iteration it is equal to downloads.html, and in the final iteration it is equal to tech-support.html. At the end of the body of the foreach loop, the extra blank line is appended to the data object and the program sleeps for 30 seconds. This is the delay time to allow the user time to read the contents of the current page that was just sent.Following the foreach loop, one more body of data is sent with the lines
print "--ThisRandomString\n"; print "Content-type: text/html\n\n"; # Windows users need to change the string "$path/home.html" to # "$path\\home.html" open(TEMPLATE, "$path/home.html") || die "Content-type: text/html\n\nCannot open HTML files!"; @template = <TEMPLATE>; close(TEMPLATE); print @template; print "\n\n";
Then the ending boundary is sent to signal the end of the data being sent. After the last data block is sent, the guided tour returns the user to the home page at the end of the tour. The home.html file is opened, the contents are read into the @template array, and then the contents of the @template array are sent to the user's Web browser. Running the server push guided tour produces HTML pages that look the same as those shown in Figures 8.9, 8.10, and 8.11.
Simple Animation
One nice feature of server push is that you can use it for single images instead of entire documents. If you place the call to the server push CGI script within the <IMG> tag, the server can push down new images that replace the previous image. This creates a flip-book style animation. Flip books are small books that contain one image per page. The image on each page is only slightly different than the image on the previous page. When you flip through the book, the images seem to move; you see a rudimentary animation.Using this feature of server push, you can create animations for your Web pages. You just have to create the image files, place an <IMG> tag in your Web page to call your animation CGI script, and create a CGI script that pushes down the image files. For this example, you use the 13 images shown in Figure 8.12. These smiley faces are characters used in the Palace chat software distributed by Time Warner. To learn more about the Palace software, or to download a free copy of the Shareware, visit their Web site at http://www.thepalace.com.
Figure 8.12: The Images for the Animation example After creating all the image files for your animation, you can create the HTML page in which the animation will be played. Listing 8.10 contains the HTML code for the animation.html file (animate.htm if your system limits you to an eight-character file name and a three-character extension). Notice how the CGI script is being called from within the <IMG> tag.
With the images created and the HTML file ready, you just need to create the animate.pl script file. As with the server push guided tour, the script first needs to send the multipart/x-mixed-replace header to the Web browser. You do this with the line
print "Content-type: multipart/x-mixed-replace;boundary=ThisRandomString\n\n";
Listing 8.10: The animation.html File <HTML> <HEAD> <TITLE>Animation Example</TITLE> </HEAD> <BODY BGCOLOR="#FFFFFF"> This animation shows the 13 facial expressions the Palace's default avatar can use. An avatar is a graphical representation of yourself in a graphical social environment. The Palace is a 2D graphical social environment that works on the Internet. For more information about the Palace, visit their Web site, http://www.thepalace.com. <CENTER> <IMG HEIGHT=44 WIDTH=44 SRC="/cgi-bin/animate.pl"> </CENTER></BODY> </HTML>
Next you need a loop to send the header and data body for each image that is being sent. Because there are 13 images, you can use a for loop that will execute 13 times. Here is the for loop for the animation script:
for($i=1 $i<=$num_images; $i++) { print "--ThisRandomString\n"; print "Content-type: image/gif\n\n"; # Windows users need to change the string # "$path/$i.gif" to "$path\\$i.gif" open(GIF, "$path/$i.gif") || die; (undef, undef, undef, undef, undef, undef,, undef, $filesize, undef, undef, undef, undef, undef) = stat(GIF); sysread(GIF, $image, $filesize); close(GIF); syswrite(STDOUT, $image, $filesize); print "\n\n"; sleep 1; }
The statement
for($i=1 $i<=$num_images; $i++) {
is composed of three parts, the initialization of the loop variable, $i=1; the loop conditional, $i<=$num_images; and the incrementation of the loop variable, $i++. The for loops execute until the conditional statement is no longer true. For this example, the loop variable $i will start at one and be incremented by one each time the loop executes until it is greater than the value stored in the $num_images variable. This variable will be set at the beginning of the animation script file, and will contain the number of images to be used. Listing 8.11 shows where this variable is set.Inside the body of the for loop, the header and data body for each image file is sent to the user's Web browser and the script then sleeps for 1 second. The two lines
print "--ThisRandomString\n"; print "Content-type: image/gif\n\n";
print the header for the image being sent to the browser. The lines
# Windows users need to change the string # "$path/$i.gif" to "$path\\$i.gif" open(GIF, "$path/$i.gif") || die; (undef, undef, undef, undef, undef, undef,, undef, $filesize, undef, undef, undef, undef, undef) = stat(GIF); sysread(GIF, $image, $filesize); close(GIF); syswrite(STDOUT, $image, $filesize);
open the image file, read the contents of the image file into the $image variable, close the image file, and output the contents of the $image variable to the user's Web browser. After sending the body of the data, you need to send the extra blank line and tell the script to sleep for a second. You do so with the following two lines:
print "\n\n"; sleep 1;
Listing 8.11 contains all the code for the animate.pl script. Notice that the variables have been set at the beginning and that the ending boundary is sent to the user's Web browser at the end. You need to change the $path variable to be valid for your system. Also, Windows users should remove the first line of the code and change the string in the open statement. Figure 8.13 shows how the Web page appears in Netscape at the end of the animation loop.
Listing 8.11: The animate.pl Script #!/usr/local/bin/perl # All users need to change the $path variable to a valid # path for their system. Windows users need to use a # path in the form $path = "c:\\robertm\\gifs"; $path = "/users/robertm/gifs"; $num_images = 13; $|=1; print "Content-type: multipart/x-mixed- replace;boundary=ThisRandomString\n\n"; for($i=1 $i<=$num_images; $i++) { print "--ThisRandomString\n"; print "Content-type: image/gif\n\n"; # Windows users need to change the string # "$path/$i.gif" to "$path\\$i.gif" open(GIF, "$path/$i.gif") || die; (undef, undef, undef, undef, undef, undef,, undef, $filesize, undef, undef, undef, undef, undef) = stat(GIF); sysread(GIF, $image, $filesize); close(GIF); syswrite(STDOUT, $image, $filesize); print "\n\n"; sleep 1; } print "--ThisRandomString--\n\n";
Figure 8.13: The Animation page