From: Brion Vibber Date: Mon, 11 Dec 2006 01:51:21 +0000 (+0000) Subject: * (bug 8148) Handle non-removable output buffers gracefully when cleaning X-Git-Tag: 1.31.0-rc.0~54943 X-Git-Url: http://git.cyclocoop.org/%24image?a=commitdiff_plain;h=a7dfa5da711c337f61333f17a6cb737147e1a09b;p=lhc%2Fweb%2Fwiklou.git * (bug 8148) Handle non-removable output buffers gracefully when cleaning buffers for HTTP 304 responses, StreamFile, and Special:Export. Duplicated code merged into wfResetOutputBuffers() and wfClearOutputBuffers() --- diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 6b8de8ec32..185a6b8cb6 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -263,6 +263,9 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN * Removed a redundant tag from diff pages that was causing display issues for some users * (bug 8203) The keyboard shortcut for "log out" was removed, because users were pressing it when they intended to press the shortcut for "preview". +* (bug 8148) Handle non-removable output buffers gracefully when cleaning + buffers for HTTP 304 responses, StreamFile, and Special:Export. + Duplicated code merged into wfResetOutputBuffers() and wfClearOutputBuffers() == Languages updated == diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 4887653599..8ebdc2991b 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -1079,6 +1079,64 @@ function wfHttpError( $code, $label, $desc ) { "

\n"; } +/** + * Clear away any user-level output buffers, discarding contents. + * + * Suitable for 'starting afresh', for instance when streaming + * relatively large amounts of data without buffering, or wanting to + * output image files without ob_gzhandler's compression. + * + * The optional $resetGzipEncoding parameter controls suppression of + * the Content-Handler header sent by ob_gzhandler; by default it + * is left. See comments for wfClearOutputBuffers() for why it would + * be used. + * + * Note that some PHP configuration options may add output buffer + * layers which cannot be removed; these are left in place. + * + * @parameter bool $resetGzipEncoding + */ +function wfResetOutputBuffers( $resetGzipEncoding=true ) { + while( $status = ob_get_status() ) { + if( $status['type'] == 0 /* PHP_OUTPUT_HANDLER_INTERNAL */ ) { + // Probably from zlib.output_compression or other + // PHP-internal setting which can't be removed. + // + // Give up, and hope the result doesn't break + // output behavior. + break; + } + if( !ob_end_clean() ) { + // Could not remove output buffer handler; abort now + // to avoid getting in some kind of infinite loop. + break; + } + if( $resetGzipEncoding ) { + if( $status['name'] == 'ob_gzhandler' ) { + // Reset the 'Content-Encoding' field set by this handler + // so we can start fresh. + header( 'Content-Encoding:' ); + } + } + } +} + +/** + * More legible than passing a 'false' parameter to wfResetOutputBuffers(): + * + * Clear away output buffers, but keep the Content-Encoding header + * produced by ob_gzhandler, if any. + * + * This should be used for HTTP 304 responses, where you need to + * preserve the Content-Encoding header of the real result, but + * also need to suppress the output of ob_gzhandler to keep to spec + * and avoid breaking Firefox in rare cases where the headers and + * body are broken over two packets. + */ +function wfClearOutputBuffers() { + wfResetOutputBuffers( false ); +} + /** * Converts an Accept-* header into an array mapping string values to quality * factors diff --git a/includes/OutputPage.php b/includes/OutputPage.php index bbb068b61d..a314b554dc 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -144,10 +144,12 @@ class OutputPage { $this->sendCacheControl(); wfDebug( "$fname: CACHED client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp ; site $wgCacheEpoch\n", false ); $this->disable(); - // Don't output compressed blob - while( $status = ob_get_status() ) { - ob_end_clean(); - } + + // Don't output a compressed blob when using ob_gzhandler; + // it's technically against HTTP spec and seems to confuse + // Firefox when the response gets split over two packets. + wfClearOutputBuffers(); + return true; } else { wfDebug( "$fname: READY client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp ; site $wgCacheEpoch\n", false ); diff --git a/includes/SpecialExport.php b/includes/SpecialExport.php index 26ca7effab..5e6d6d8dda 100644 --- a/includes/SpecialExport.php +++ b/includes/SpecialExport.php @@ -83,12 +83,7 @@ function wfSpecialExport( $page = '' ) { // Cancel output buffering and gzipping if set // This should provide safer streaming for pages with history - while( $status = ob_get_status() ) { - ob_end_clean(); - if( $status['name'] == 'ob_gzhandler' ) { - header( 'Content-Encoding:' ); - } - } + wfResetOutputBuffers(); header( "Content-type: application/xml; charset=utf-8" ); $pages = explode( "\n", $page ); diff --git a/includes/StreamFile.php b/includes/StreamFile.php index 6418269862..949422d691 100644 --- a/includes/StreamFile.php +++ b/includes/StreamFile.php @@ -22,12 +22,7 @@ function wfStreamFile( $fname ) { header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $stat['mtime'] ) . ' GMT' ); // Cancel output buffering and gzipping if set - while( $status = ob_get_status() ) { - ob_end_clean(); - if( $status['name'] == 'ob_gzhandler' ) { - header( 'Content-Encoding:' ); - } - } + wfResetOutputBuffers(); $type = wfGetType( $fname ); if ( $type and $type!="unknown/unknown") {