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