From a5ef6c3b2070f45f88b9d5942cd974c9c26376c5 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 26 Jun 2011 11:01:46 +0200 Subject: [PATCH] [ticket/10188] Prevent semi-compressed output When a non-fatal error occurs at the beginning of the script before any custom error handler is set one of two situations can be encountered: 1) if the ini option output buffer is disabled: - headers are sent to the http client - the error message is output 2) if the ini option output_buffer is enabled or the script is run within an ob_start()/ob_end() wrapper: - the error message is written to the output buffer Once the script reaches page_header() phpbb starts gzip compression if enabled. This is done through ob_start with a ob_gzhandler as a callback. The compression is skipped if headers have already been sent. In situation 1) the error message sent in plain text comes with headers and this gzip compression is skipped. The client receives a plaintext version of the page. However in situation 2) headers have not been sent yet and the rest of the page will be compressed. The result is a plaintext error message followed by compressed output. The client does not understand this output resulting in either an error message or simply a blank page in the browser. In addition to the above situation this problem occurs with errors that are triggered after the custom error handler is loaded. The problem has been noticed before, and a workaround was found. The error handler would call ob_flush() for particular configuration settings before outputting the error message. This resulted in headers being sent when output buffering was enabled thus disabling gzip compression for the rest of the page. The constraints under which ob_flush() was called were lessened over time whenever a new case was found that would trigger this problem. Eventually ob_flush() would be called even when code causing an E_NOTICE was simply run within an ob_start/ob_end. This makes it impossible to use output buffering to retrieve the content of an error message without prohibiting the page from setting headers afterwards. This commit removes all flushing in msg_handler completely and instead fixes the problem for both errors before and after the error handler is registered. GZIP compression is only enabled if there is at most one level of output buffering (e.g. the output_buffer php.ini option is enabled) and if there has not yet been any output in this buffer. This should avoid any partial output compression. PHPBB3-10188 --- phpBB/includes/functions.php | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/phpBB/includes/functions.php b/phpBB/includes/functions.php index a89b47b170..d05cccc440 100644 --- a/phpBB/includes/functions.php +++ b/phpBB/includes/functions.php @@ -3758,21 +3758,6 @@ function msg_handler($errno, $msg_text, $errfile, $errline) if (strpos($errfile, 'cache') === false && strpos($errfile, 'template.') === false) { - // flush the content, else we get a white page if output buffering is on - if (ob_get_level() > 0) - { - @ob_flush(); - } - - // Another quick fix for those having gzip compression enabled, but do not flush if the coder wants to catch "something". ;) - if (!empty($config['gzip_compress'])) - { - if (@extension_loaded('zlib') && !headers_sent() && !ob_get_level()) - { - @ob_flush(); - } - } - // remove complete path to installation, with the risk of changing backslashes meant to be there $errfile = str_replace(array(phpbb_realpath($phpbb_root_path), '\\'), array('', '/'), $errfile); $msg_text = str_replace(array(phpbb_realpath($phpbb_root_path), '\\'), array('', '/'), $msg_text); @@ -4332,7 +4317,21 @@ function page_header($page_title = '', $display_online_list = true, $item_id = 0 // gzip_compression if ($config['gzip_compress']) { - if (@extension_loaded('zlib') && !headers_sent()) + // to avoid partially compressed output resulting in blank pages in + // the browser or error messages, compression is disabled in a few cases: + // + // 1) if headers have already been sent, this indicates plaintext output + // has been started so further content must not be compressed + // 2) the length of the current output buffer is non-zero. This means + // there is already some uncompressed content in this output buffer + // so further output must not be compressed + // 3) if more than one level of output buffering is used because we + // cannot test all output buffer level content lengths. One level + // could be caused by php.ini output_buffering. Anything + // beyond that is manual, so the code wrapping phpBB in output buffering + // can easily compress the output itself. + // + if (@extension_loaded('zlib') && !headers_sent() && ob_get_level() <= 1 && ob_get_length() == 0) { ob_start('ob_gzhandler'); }