From 42566379dc3c4fd01a73067215da4a7ca18f9c17 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 20 Feb 2016 11:51:33 -0800 Subject: [PATCH] Fix HTTP body decompression in PHP 5.6 Summary: Ref T10264. Under PHP 5.6, you are no longer allowed to use `compress.zlib://php://input` as an argument to either `fopen()` or `file_get_contents()`. Instead, open `php://input` as a file handle, then add `zlib.inflate` as a stream wrapper. This requires some level of magic to work properly. Test Plan: First, I constructed a synthetic gzipped payload by typing some words into a file and using `gzcompress()` to compress it. Then I used a `curl` command like this to make requests with it: ``` $ curl -X POST -H "Content-Length: 66" -H "Content-Type: text/plain" -H "Content-Encoding: gzip" --data-binary @payload.deflate -v http://127.0.0.1/ ``` I modified Phabricator to just dump the raw request body and exit, and reproduced the issue under PHP 5.6 (no body, error in log) by brining up a micro instance in EC2 and installing php56 on it. After this patch, it dumped the body properly instead, and PHP 5.5 also continued worked properly. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10264 Differential Revision: https://secure.phabricator.com/D15314 --- support/PhabricatorStartup.php | 37 +++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/support/PhabricatorStartup.php b/support/PhabricatorStartup.php index bc7daf4ea6..5174767e28 100644 --- a/support/PhabricatorStartup.php +++ b/support/PhabricatorStartup.php @@ -135,13 +135,40 @@ final class PhabricatorStartup { $encoding = null; } - if ($encoding === 'gzip') { - $source_stream = 'compress.zlib://php://input'; - } else { - $source_stream = 'php://input'; + $input_stream = fopen('php://input', 'rb'); + if (!$input_stream) { + self::didFatal( + 'Unable to open "php://input" to read HTTP request body.'); } - self::$rawInput = (string)file_get_contents($source_stream); + if ($encoding === 'gzip') { + $ok = stream_filter_append( + $input_stream, + 'zlib.inflate', + STREAM_FILTER_READ, + array( + 'window' => 30, + )); + + if (!$ok) { + self::didFatal( + 'Failed to append gzip inflate filter to HTTP request body input '. + 'stream.'); + } + } + + $input_data = ''; + while (!feof($input_stream)) { + $read_bytes = fread($input_stream, 16 * 1024); + if ($read_bytes === false) { + self::didFatal( + 'Failed to read input bytes from HTTP request body.'); + } + $input_data .= $read_bytes; + } + fclose($input_stream); + + self::$rawInput = $input_data; }