tokuhirom/p5-cgi-emulate-psgi — pull request #4 by wu-lee (2014-02-27 16:17:24+00:00)
Title: Respect an NPH script's HTTP status line

Hi,

I have some Non-parsed Header CGI scripts which I've been serving via Plack::App::CGIBin. I notice that when they return non-200 statuses, this is ignored and overridden with a 200 Ok status.

Currently, CGI::Parse::PSGI::parse_cgi_output will return a 200 OK status when passed a header like this:

HTTP/1.0 400 Bad Request
Content-Type: text/plain

Invalid parameters

I hope you agree, this doesn't seem sensible.

Checking the source, I can see that this is because the HTTP::Response is used to parse the output, but the resulting instance's ->code attribute is ignored - only the Status: header field is used, and if it is not present the default of 200 is used.

The NPH CGI scripts I am serving return their status in the HTTP status line, not in a "Status:" header field. This seems reasonable, because "Status:" isn't actually a valid HTTP header field. So far as I can see it is only defined in the CGI spec, RFC 3875, and not the HTTP spec, 2616. The CGI spec says the "Status:" header field is meant to be removed before the server sends the response on to the client.

My pull request attempts to make parse_cgi_output do the right thing in this case. It uses the status from the HTTP header line (HTTP::Response->code) and use it as a default if there is no overriding Status: header field. Tests included.

I occurred to me that someone may be relying on the old behaviour and expecting an error code in the HTTP status line to be ignored. So I've provided a way of reverting to the legacy behaviour, by supplying an option hash as the second argument:

parse_cgi_output(\$output, {ignore_status_line => 1});

This argument is ignored by the current implementation and so has no effect - therefore, it can be used to ensure the old behaviour is preserved in any case. Users of Plack::App::CGIBin can't access this option directly without modifications to that module, but as a workaround they could set:

$CGI::Parse::PSGI::DEFAULT_OPTS{ignore_status_line} = 1.