perljunkymunky has asked for the wisdom of the Perl Monks concerning the following question:

I thought that I could accomplish this with the following code, but it still waits till the script is complete to display the web page.
#!/usr/local/bin/perl use strict; use CGI qw/:standard/; $| = 1; #I was hoping that this would make it that stuff #stuff was sent to the browser each time rather than #waiting till the end of the script ################### MAIN ########## my $cgi = CGI->new(); print $cgi->header() . "\n"; html_1(); some_long_process(); html_2(); ############################################ sub html_1 { print "\n"; print "your stuff is being processed<br>\n"; } sub html_2{ print "\n"; print "your process is done<br>\n"; }
Any help is much appreciated!
thanks,
perljunkymunky

Replies are listed 'Best First'.
Re: Sending html content to the browser as the script runs
by ikegami (Patriarch) on Aug 03, 2006 at 00:34 UTC

    Read up on NPH scripts.

    Update:

    While NPH would do what you said in the subject (unless the browser waits for the whole page to be received before rendering the page), it's not well suited to provide a "Please Wait" screen. Specifically, it doesn't allow you to remove any "Please Wait" messages you have sent* (without using ill-supported Server Pushes).

    What you want to do is usually done as follows:

    1. Start an external process to perform the requested task. (Alternatively, you can send the request to an existing process.)
    2. Give a please wait response to the client, and have the client Refresh itself after a time interval.
    3. When the client refresh itself, check if the task is done. If the task is not done, repeat the last step.
    4. Display the result.

    Instead of using the HTTP Refresh header, you can use any of Java, Flash or JavaScript (AJAX) to check whether the task has been completed or not. I wouldn't use them.

    Update:

    * – I suppose you could send JavaScript which modifies the document being viewed.

      ... it's not well suited to provide a "Please Wait" screen. Specifically, it doesn't allow you to remove any "Please Wait" messages you have sent* (without using ill-supported Server Pushes).

      I'll be the first to admit that server-push isn't well supported in most browsers, however, for any CSS aware browser, if they have javascript turned on, you can get around the problem.

      Basically, you do the following logic:

      print a block of text in a named div loop: (wait) emit javascript to set the last named div to 'hidden' print the next block of text in a named div (repeat the loop)

      I'll admit, it's not the most friendly way of doing things, but it doesn't require java or flash to be installed, and it will still get to the results (although, leave other stuff on the screen), even if the javascript isn't turned on. (assuming you don't load the text to come up in a hidden element, and then use javascript to unhide it as to hide the otherone)

Re: Sending html content to the browser as the script runs
by rodion (Chaplain) on Aug 03, 2006 at 00:56 UTC
    Yes, as ikegami says, what you need (if you're not going the AJAX route) is an NPH script (stands for "No Parse Head"). I took a look at the documentation pointed to in the link above (to the NPH scripts section of the CGI.pm docs), and one of the things it doesn't mention is how and NPH script solves your problem. Most web servers will normally gather all the output together, attach headers, and then send the headers and contents to the client. You don't see anything until all the data is produced by the CGI. With NPH, the web server doesn't take on that responsibility, and in that situation it serves up data as it gets it from the CGI. If your web page has content which can be displayed as-you-go, it does.

    One thing to not miss in the docs. On some web servers, the script has to start with the string "nph-". It's most awkward, but on those servers there is no way around it.

      Through my browser, it seems to dump to the client all at one time still at the end. Would you guys mind scanning my code real quick and see if you notice anything suspicious? Should I be putting more in the header()?
      thanks,
      PJM
      #!/usr/local/bin/perl #script: nph-test_script_jmj.cgi use strict; use CGI qw(:standard -nph); $| = 1; ################### MAIN ########## CGI->nph(1); my $cgi = CGI->new(); print $cgi->header(-nph=>1); html_1(); print "my second is: " . mytime() . "<br>\n"; sleep 3; print "my second is: " . mytime() . "<br>\n"; html_2(); ############################################ sub html_1 { print "\n"; print "processing file<br>\n"; } sub html_2 { print "\n"; print "done processing file<br>\n"; } sub mytime { my ($sec) = localtime; return $sec; }
        I ran your script as is and got:
        processing file my second is: 42 my second is: 45 done processing file
        What server software are you using? Not all servers support NPH scripts.

        jdtoronto

Re: Sending html content to the browser as the script runs
by jdtoronto (Prior) on Aug 03, 2006 at 01:57 UTC
    When an HTTP server is building a response it ultimately has to end up with a length value in the header for the document. Thus the document cannot be sent until it is complete and the content-length is known.

    The NPH (non-parsed header) does not need the content-length header before the document can be parsed by the browser.

    BUT - and isn't there always a BUT

    NPH is now pretty dated technology. The NPH script means that you have to craft the entire header yourself and send it. NPH means that the server does not parse the headers you pass - it just passes yours on. However, some HTML elements are not able to be rendered until they are completely available to the browser - tablesbeing one example. Part of a table cannot be rendered until the entire table is transferred. Generally speaking - using a refresh or some JavaAcript will be more reliable.

    jdtoronto

Re: Sending html content to the browser as the script runs
by jZed (Prior) on Aug 03, 2006 at 00:52 UTC
    This might not be the answer you're looking for, but it's one way to accomplish the task: use AJAX to print html1, then call the long_process with a JavaScript function that prints html2 as its onComplete action, possibly even replacing html1 if desired.
Re: Sending html content to the browser as the script runs
by jaa (Friar) on Aug 03, 2006 at 09:05 UTC

    This is a naff suggestion - I don't have the env to test it..., but it should be easy for you to try...

    sub html_1 { printnow("your stuff is being processed"); } sub html_2{ printnow("your process is done"); } # 8K cache buster... sub printnow { print @_, "<br>", " " x 8192, \n",; }

    It assumes that you are using Apache, and that it has the usual 8K bucket cacheing.

    So I don't really propose that you use it, but it may illustrate that the problem with immediate user feedback is in fact, Apache 8K cache.

    An alternate solution is to have an IFRAME in your code, and fetch the start page into the IFRAME, and then on some reasonable timed basis, fetch a progress / completion page. This is pretty easy for Javascript on most modern browsers.

    Regards, Jeff