My script receives gzipped data from a desktop application via POST, which it retrieves and processes from $HTTP_RAW_POST_DATA. In PHP 5.2.16 (ISAPI), $HTTP_RAW_POST_DATA is correctly being populated with the expected binary data. After upgrading to PHP 5.3.9 (FastCGI), $HTTP_RAW_POST_DATA is not defined. How can I get the data?
I’m running IIS 5.1 under 32-bit Windows XP SP3.
The docs regarding $HTTP_RAW_POST_DATA state that it ‘is not available with enctype="multipart/form-data"’, but I’m not using that content type. (Recall that the same code works fine under PHP 5.2, so content type would have been a problem then, too.)
When I enabled the always_populate_raw_post_data ini directive, nothing changes. phpinfo() reports that setting as "On", but the variable is still not being set.
The docs also suggest that the preferred method to obtain this data is to read it from the php://input stream instead. I tried that as follows:
$HTTP_RAW_POST_DATA = file_get_contents('php://input');
but the script just hangs on that line, presumably because it’s waiting for data (i.e., no End-Of-File sent). Even if I limit the maxlength to a value as small as one byte as follows, it still hangs.
$HTTP_RAW_POST_DATA = file_get_contents('php://input', false, null, -1, 1);
The only time it doesn’t hang on that line is when I set the maxlen to 0, which means it doesn’t try to read anything but also isn’t helpful. 🙂
If $HTTP_RAW_POST_DATA won’t populate when I force it to, and I can’t get anything from php://input, how can I get the data?
Is the fact that php://input is apparently empty indicative of a difference between ISAPI and FastCGI? I haven’t read anything suggesting that raw POSTs behave differently or are lost under FastCGI.
Here are some relevant portions of $_SERVER. All of these values are identical between running the script with PHP 5.2 and PHP 5.3.
[CONTENT_LENGTH] => 4294967295
[CONTENT_TYPE] => application/client-gzip
[HTTP_HOST] => 127.0.0.1
[HTTP_CONTENT_TYPE] => application/client-gzip
[HTTP_TRANSFER_ENCODING] => chunked
[HTTP_ACCEPT_ENCODING] => gzip
[HTTP_EXPECT] => 100-continue
[REQUEST_METHOD] => POST
[SERVER_NAME] => 127.0.0.1
[SERVER_PORT] => 80
[SERVER_PROTOCOL] => HTTP/1.1
(Note: I know a 4GB content length looks odd, but because it’s using chunked encoding, CONTENT_LENGTH is ignored.)
Since the CONTENT_LENGTH header is not used, is there any way for me to verify that the data is indeed being received by PHP somewhere and not lost by IIS?
Since the data is being sent from a binary library in a desktop client, changing the format of the POST is not an option. Regardless of the pros/cons of using $HTTP_RAW_POST_DATA like this, this is how I must do it. It worked like this before, so I fully expect it to work now. Any insight is sincerely appreciated!
Update
Here’s a simple script to exemplify my problem. There are two files:
postsend.php
<?php
$headers = array(
"Content-Type: application/client-gzip",
// "Transfer-Encoding: chunked",
);
$text = 'This text is superior to your text even though it is useless.';
$data = gzdeflate($text);
echo "text: $text<br />gzip: $data<br />";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://localhost/server/postreceive.php");
curl_setopt($ch, CURLOPT_USERPWD, "user:pass");
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$result = curl_exec($ch);
echo $result;
postreceive.php
<?php
if (!empty($HTTP_RAW_POST_DATA)) {
printf(
'HTTP_RAW_POST_DATA set!<br />gzip:%s<br />text: %s',
$HTTP_RAW_POST_DATA,
gzinflate($HTTP_RAW_POST_DATA)
);
} else {
echo 'No raw data. :-(';
}
Under PHP 5.3 (FastCGI), this works when I run postsend.php, and the output is:
text: This text is superior to your text even though it is useless.
gzip: ÉÈ,V(I(QÒÅ¥©E™ùE %ù •ù¥E‰Ô²Ô<…’ŒüÒô …L°ºÒâÔœÔâb=
HTTP_RAW_POST_DATA set!
gzip: ÉÈ,V(I(QÒÅ¥©E™ùE %ù •ù¥E‰Ô²Ô<…’ŒüÒô …L°ºÒâÔœÔâb=
text: This text is superior to your text even though it is useless.
However, if I uncomment the line with the Transfer-Encoding header, everything just hangs when I run postsend.php. So, receiving chunked encoding seems to be breaking it.
Is there some IIS or FastCGI configuration that I’m missing which would allow this to work when receiving chunked encoding? (If so, is this now more appropriate as a ServerFault question?) I’ve seen settings that enable sending chunked encoding but not receiving chunked encoding.
As eventually identified in bug report #60826, this problem is caused beyond PHP in the web stack.
It is a shortcoming of how FastCGI is implemented in various web servers.