[Date Prev][Date Next][Thread Prev][Thread Next] [Search] [Date Index] [Thread Index]

[MacPerl-WebCGI] Re:how to prevent time-out with slow outputstream



Christoph Grunau wrote:

>>>I have  a MacPerl CGI (running under MacOS 9, QPQ2.1) that sanns a 
>>>number of files for patterns and generates a dynamic web-page with 
>>>the result. As long as the number of files to scan is relatively 
>>>small everything works fine.  However, when I screen about 2500 
>>>files the CGI takes about 1 min to generate the output. I set the 
>>>server time-out to 300 sec which should be enough but usually the 
>>>clients time-out much earlier. Is there a way to prevent this?

I replied:

>>solution is to return the output a bit at a time, a technique 
>>sometimes called "server push"...
>>If you are still interested, let us know and I will post a "step by step".

Christoph Grunau responded:

>So it seems to me the safest way would be to write the output to a 
>temporary html-file and let a web-page reload this page after 2 min 
>or so.

I have not tried that approach, but it seems worth a try. 
Complications I can see include:
1) If you have multiple simultaneous users, you will have to think 
how to manage their different temporary files.
2) If the users are unselected (e.g. if this is a public server), I'm 
not sure that all browsers support auto-refresh so your CGI might not 
work for all users.

> However, I would be interested in the step-by-step instruction [for 
>server push].

Oy!  Although I have done this many times in the past, when I went to 
reproduce and test my server push scripts, I found the situation much 
more complicated than I had remembered and I spent WAY more time than 
I had planned and still I am not satisfied.  What follows is my 
current state of confusion: take it with all the salt you see fit.

I am attaching three example scripts.  They are derived from 
Demo.acgi which is part of the MacPerl distribution, provided with 
the CGI glue, written by Matthias Neeracher. I think I need to 
provide some explanation before giving them, however.

The major problem I ran into was that how server push works now seems 
to be browser-dependent.  I am currently unable to get Netscape 6 to 
run on my iMac.  Internet Explorer 5.0/Macintosh never displays as 
expected; no matter what I do, nothing appears on the screen until 
the entire output is received.  Internet Explorer 5.5/Windows 
displays the output all at once the first time you run the script, 
but if you reload, output appears gradually.  Netscape 4.7, Windows 
or Mac, works as I had expected.  Everything below refers to the 
behavior with Netscape 4.7.

Most commonly, server push is used for things like animated GIFs. 
The idea here is that the server sends a set of images where each 
replaces the previous one and the appearance of motion occurs.  In 
that case, you want new content to replace the old content.  In my 
case (and I think your case), what is required is to prevent 
time-outs (either by software or liveware), in which case you want 
the content to append to what's already there; you want the page to 
appear gradually as output is generated and transmitted.  The second 
behavior is, AFAIK, a simple subset of the first.  However, most 
examples (including Matthias's) are of the more complex, "animated 
GIF" kind.

To get server push to work, the Perl script has to return its output 
as it is generated rather than all at once, the Web Server needs to 
return that output as it is generated to the browser, and the browser 
needs to display that output as it receives it.  This is not the 
default behavior either of (Unix) Perl or of most Web servers.  I 
suspect that the problem with IE5/Mac is that its (default) behavior 
is not to display output gradually.  (An interesting implication of 
this is that using server push should prevent IE5/Mac from timing 
out, but perhaps not its user.)

Under MacOS, things are slightly more complicated in that there is 
one more player; the CGI glue.  If you write a Mac CGI using 
AppleScript (or many other languages), you communicate with the Web 
browser directly using AppleEvents, a completely different API model 
than the standard in/standard out used under Unix.  In MacPerl, to 
maintain portability, we use an intermediary bit of software to allow 
Unix CGI scripts to run with MacPerl, often with no changes at all. 
When you save a MacPerl script as a "CGI Script" you are in fact 
creating a small, standalone application which contains the text of 
your script as a resource.  When this program runs, it accepts the 
AppleEvents from the Web Server, and then runs MacPerl, sending it 
the script, the data from the Web Server, and appropriate parameters 
such that a Unix-style CGI will work just fine.  Thus, for server 
push to work on the Mac, the CGI glue has to pass data appropriately 
as well.

Most of the examples of Server Push you will see on the web are for 
the Unix family of operating systems, so let me start by explaining 
how that works in preparation for showing the differences with the 
MacOS.  Getting Perl to return output gradually to the Web Server is 
a piece of cake; all you have to do is to set the special variable $| 
to some true value (traditionally, 1).  The way you signal to the 
ever popular Apache Web Server (e.g. under Linux) that it should 
return output to the client gradually is by using an appropriate name 
for your CGI script (!); the script needs to start with the prefix 
nph-.  This prefix stands for "non-parsed headers" which tells Apache 
that you are providing the entire headers for the HTTP transaction 
and that Apache should make no changes in that header; normally, a 
CGI only returns the document type information (e.g. Content-type: 
text/html) and Apache does the rest.  So what, you may ask?  For no 
reason I can figure, when you tell Apache (via the script name) that 
you are supplying the headers, Apache decides to also return the 
output as received rather than all at once; you have server push. 
The effect this has on your Perl coding is that your Perl script now 
needs to send a complete set of headers.

Reading the documentation, things should be pretty similar under 
MacOS except that most MacOS Web servers decide about headers not 
from the filename but by examining the first few lines of output; if 
your CGI supplies a truncated header, the server will complete it, if 
not, not.  In fact, I find, using Quid Pro Quo version 2.1 and the 
current MacPerl release, remarkably little is required.  I find that 
server push works even if you DON'T set $!, and even if you DON'T 
provide full headers.  You DO have to do two things that you DON'T 
have to do with Unix, however.  The first is that you have to name 
your MacPerl CGI using the suffix .acgi, not the normal suffix of 
.cgi.  This instructs the Web server to communicate with the CGI glue 
in a way that allows server push.  It also sets up your server for 
instability as I have noted before.  The second thing you have to do 
is to overtly flush output in your MacPerl script by reading from 
<STDIN>.  Given these rules, the simplest server push script I can 
get to work is as follows:

------------------------------------------------------------------------------

#!/usr/bin/perl
#
# serverpush_simple.acgi
# by David Steffen, 12-15-2000
# The simplest HTML server push script I can get to run on QPQ
#
# This script appends a series of numbers to the browser so that each number is
# added to the end of the page after a delay

print "Content-type: text/html\n\n";

print "<html><head><title>Test</title></head><body>\n";

print "<h1>Let the Test Begin.</h1>\n";

for ($i = 0; $i++<3; ) {
     print"<p>$i</p>\n";
     <STDIN>;             # read from STDIN to flush output
     sleep(2);            # a delay to illustrate progressive output
}

print "</body></html>\n";

__END__

------------------------------------------------------------------------------

Interestingly, I cannot get the above script to work if I try to send plain
text rather than HTML; it never exits.  Output _is_ returned (and displayed
in Netscape 4.7), but the browser keeps waiting for more.

Perhaps the above script is too simple; it may fail on Mac Web Servers other
than QPQ 2.1.  A somewhat more complex script that follows more of the rules
is as follows:

------------------------------------------------------------------------------

#!/usr/bin/perl
#
# serverpush_standard.acgi
# by David Steffen, 12-15-2000
# This server push script follows the rules for MacPerl server push, AFAIK
#
# This script appends a series of numbers to the browser so that each number is
# added to the end of the page after a delay

$| = 1;                # This forces Perl to "flush" output each time you print
$eol = "\015\012";     # Correct HTTP newline; this matters in headers.
$boundary = "";        # Unused in this version

# Output a complete header.
print "HTTP/1.0 200 OK$eol";
print "Server: Quid Pro Quo$eol";
print "MIME-Version: 1.0$eol";
print "Content-type: text/html$eol$eol";

print "<html><head><title>Test</title></head><body bgcolor=\"#FFFFFF\">$eol";

print "<h1>Let the tests begin!</h1>$eol";

for ($i = 0; $i++<3; ) {
     print "<p>$i</p>$eol";
     <STDIN>; # The secret magic for making server push work on a Mac
     sleep(2); # wait 2 seconds
}

print "</body></html>$eol";

__END__

------------------------------------------------------------------------------
The script doesn't work with plain text either :-|

My third and final example illustrates conventional server push where the
content is sent as multipart MIME.  In this case, each time you flush the
new output REPLACES the old.  Most commonly, you would call this from within
an <img /> tag, with the source=YourCGI.acgi.  Here, I am outputing to
the main window, which causes the whole contents of the window to be
replaced.
------------------------------------------------------------------------------

#!/usr/bin/perl
#
# serverpush_multipart.acgi
# by David Steffen, 12-15-2000
#
# This script displays a series of numbers to the browser so that each number
# replaces the previous number
# Comments as in the previous scripts except where noted

$| = 1;
$eol = "\015\012";
# The boundary string is what separates the different
# chuncks of content.  It consists of an arbitrary string
# which is declared in the initial header followed by
# a description of the MIME type of the next chunk of
# content.
$boundary = "--EOM${eol}Content-type: text/html$eol$eol";

print "HTTP/1.0 200 OK$eol";
print "Server: Quid Pro Quo$eol";
print "MIME-Version: 1.0$eol";
# The next line is what causes each iteration of content to replace
# the previous content
print "Content-Type: multipart/x-mixed-replace;boundary=EOM$eol$eol";

# iterations separated by the $boundary string
print $boundary, $eol;

print "<html><head><title>Test</title></head><body bgcolor=\"#FFFFFF\">$eol";

print "<h1>Let the tests begin!</h1>$eol";

for ($i = 0; $i++<3; ) {
     print $boundary, "</p>$i</p>$eol";
     <STDIN>; # This line essential for MacPerl, apparently neutral for Linux
     sleep(2);
}

print "</body></html>$eol";

print "--EOM--$eol"; # signals the end of the last bit of content

__END__

------------------------------------------------------------------------------

hth

-David Steffen-


David Steffen, Ph.D.
President, Biomedical Computing, Inc. <http://www.biomedcomp.com/>
Phone: (713) 610-9770 FAX: (713) 610-9769 E-mail: steffen@biomedcomp.com

==== Want to unsubscribe from this list?
==== Send mail with body "unsubscribe" to macperl-webcgi-request@macperl.org