http://qs321.pair.com?node_id=714883

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

Greetings!

I'm trying to debug some legacy CGI code that is not working right with some web browsers, notably Firefox. The strategy this script uses is pretty much the same one that merlyn illustrates with a simple script in one of his Linux Magazine columns:

http://www.stonehenge.com/merlyn/LinuxMag/col39.html

This script is supposed to execute a long-running command and display updates along the way. It does this by forking the original process so that the child continues on to feed data to a cache, while the parent essentially instructs the browser to check back periodically for updates.

I've installed merlyn's script locally, and it works fine when I access it with Safari, but it does not behave properly when I use Firefox: instead of displaying the updates, it only displays the final output.

At first I thought that the problem had to do with the <meta http-equiv="refresh"...> directive, but after I re-implemented the refresh using JavaScript and got exactly the same behavior as before, both from Safari and from Firefox, now I think that the problem may have to do with the fork.

The reason behind this hunch is that, if I watch the Apache access logs while I attempt to run the script, I see only two lines corresponding to this interaction: the initial POST and the very last GET; all the intervening GETs are missing (and there's nothing in the error log). More importantly, the initial POST does not appear in the logs when it is made, but several seconds later, simultaneously with the appearance of the final GET line, right around the time when the browser finally displays the results. This suggested to me that maybe the initial POST was not being deemed complete until the child from the initial fork was done.

Even if this is the case, it's not entirely clear to me why the behavior is different with Safari. (When the interaction is with Safari, all the intermediate GETs show up in the logs, and they appear at regular intervals throughout the interaction.)

If anyone has any ideas about how to troubleshoot this further please let me know.

Many thanks in advance!

the lowliest monk

Replies are listed 'Best First'.
Re: Problem w/ forking CGI script
by almut (Canon) on Oct 01, 2008 at 22:06 UTC

    In my experience, you also need to close STDERR in the child (at least with recent Apaches), not only STDOUT.

    Try adding

    ... } elsif (defined $pid) { # child does close STDOUT; # so parent can go on close STDERR; # <--- THIS ...

    (The open STDERR, ">&=1"; which you find in merlyn's code doesn't implicitly close file descriptor number 2 (stderr) under the hood ... as you can verify using strace. )

    The fact that it (seemingly) works with Safari probably has to do with different browser buffering behavior, i.e. it presumably processes partial content differently (before the HTTP request has completed). But that's just a guess — at the moment I don't have a Safari to investigate this further.  (You could check whether the individual Apache children (CGI processes) actually do terminate as expected in this case...)

      Thanks for the tip. That's good to know in general.

      The way I ultimately solved the problem was to examine the requests generated by both Safari and Firefox. One important difference was that Firefox was sending a "Keep-Alive: 300" header, while Safari wasn't. This gave me the idea of modifying the redirection to

      print redirect( -Location => self_url(), -Connection => 'close' );
      This worked.

      But your suggestion of closing STDERR also works. I'll do both from now on, for good measure. :-)

      the lowliest monk