0

I inherited maintenance of a self-written CGI application without documentation and have never met the original author. The application stopped working in Debian 8, but worked in Debian 7 and CentOS 5. The main changes were the upgrade from Apache 2.2 (used by Debian 7 & CentOS 5) to Apache 2.4 (used by Debian 8) and the upgrade from perl 5.8 (in CentOS 5) respectively perl 5.14 (in Debian 7) to perl 5.20. The problematic part boils down to the following script (a 302-redirect):

#!/usr/bin/perl
$|=1; # activate auto-flushing of stdout
use strict;
use warnings;
my $CRLF = "\015\012";
print STDOUT "Status: 302 Moved Temporarily$CRLF" .
             "Location: /does_not_matter$CRLF" .
             "URI: /does_not_matter$CRLF" .
             "Connection: close$CRLF" .
             "Content-type: text/html; charset=UTF-8$CRLF$CRLF";
close STDOUT;
while(1) {
        sleep 1;
}

The observed behavior is that the redirect never reaches the client as long as the script is still running when used with Apache 2.4, but there is no error message in Apache's error.log. I changed the client (Firefox, Chromium, wget), the Apache module (mod_cgid & mod_cgi), sent additional headers, removed the close STDOUT, removed the $|=1, replaced the $CRLF with \n and made the script fork and exit the parent process (so that Apache is no longer the parent process), all to no avail. The only things that worked: use Apache 2.2, turn the script into an NPH-CGI-script (which has to send complete HTTP headers that Apache will not modify in any way, even if they contain errors), make the script exit instead of entering an endless loop. I confirmed via tcpdump that the packages with the redirect indeed never leave the server before the script is killed. And from the Date-line in the response and the time of the eventual arrival I gather that Apache receives my output immediately (& immediately adds the Date-line to the headers), but does not send the response to the client.

Don't bother answering, I already figured the solution out by myself and will write an answer. I just want to make the solution available to others who might encounter the same problem.

user2845840
  • 360
  • 3
  • 9

1 Answers1

1

The problem was the missing message body. A 302 redirect may contain a message body (see RFC 2616, section 4.3 (Message Body): "All other responses do include a message-body, although it MAY be of zero length"), but a Content-Length-line is optional (section 4.4 of RFC 2616 says the message body length can be determined by closing the connection if the Content-Length-line is missing). Since Apache can not know whether I want to send a message body or not, it has to wait until I close the connection or actually send the message body (Apache 2.2 apparently behaved erroneously here by not waiting for the message body - or maybe close STDOUT; does not do in perl 5.20 what it did in older perl versions). The correct script should therefore look like this (verified to work both in Apache 2.2 and in Apache 2.4) - the only difference is an additional $CRLF which terminates a zero-length message body:

#!/usr/bin/perl
$|=1; # activate auto-flushing of stdout
use strict;
use warnings;
my $CRLF = "\015\012";
print STDOUT "Status: 302 Moved Temporarily$CRLF" .
             "Location: /doesNotMatter$CRLF" .
             "URI: /doesNotMatter$CRLF" .
             "Connection: close$CRLF" .
             "Content-type: text/html; charset=UTF-8$CRLF$CRLF$CRLF";
close STDOUT;
while(1) {
        sleep 1;
}

It was https://stackoverflow.com/a/8062277/2845840 that pointed me in the right direction.

Community
  • 1
  • 1
user2845840
  • 360
  • 3
  • 9