Index: trunk/phase3/RELEASE-NOTES |
— | — | @@ -263,6 +263,9 @@ |
264 | 264 | * Removed a redundant <strong> tag from diff pages that was causing display issues for some users |
265 | 265 | * (bug 8203) The keyboard shortcut for "log out" was removed, because users were pressing it |
266 | 266 | when they intended to press the shortcut for "preview". |
| 267 | +* (bug 8148) Handle non-removable output buffers gracefully when cleaning |
| 268 | + buffers for HTTP 304 responses, StreamFile, and Special:Export. |
| 269 | + Duplicated code merged into wfResetOutputBuffers() and wfClearOutputBuffers() |
267 | 270 | |
268 | 271 | |
269 | 272 | == Languages updated == |
Index: trunk/phase3/includes/OutputPage.php |
— | — | @@ -144,10 +144,12 @@ |
145 | 145 | $this->sendCacheControl(); |
146 | 146 | wfDebug( "$fname: CACHED client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp ; site $wgCacheEpoch\n", false ); |
147 | 147 | $this->disable(); |
148 | | - // Don't output compressed blob |
149 | | - while( $status = ob_get_status() ) { |
150 | | - ob_end_clean(); |
151 | | - } |
| 148 | + |
| 149 | + // Don't output a compressed blob when using ob_gzhandler; |
| 150 | + // it's technically against HTTP spec and seems to confuse |
| 151 | + // Firefox when the response gets split over two packets. |
| 152 | + wfClearOutputBuffers(); |
| 153 | + |
152 | 154 | return true; |
153 | 155 | } else { |
154 | 156 | wfDebug( "$fname: READY client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp ; site $wgCacheEpoch\n", false ); |
Index: trunk/phase3/includes/StreamFile.php |
— | — | @@ -22,12 +22,7 @@ |
23 | 23 | header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $stat['mtime'] ) . ' GMT' ); |
24 | 24 | |
25 | 25 | // Cancel output buffering and gzipping if set |
26 | | - while( $status = ob_get_status() ) { |
27 | | - ob_end_clean(); |
28 | | - if( $status['name'] == 'ob_gzhandler' ) { |
29 | | - header( 'Content-Encoding:' ); |
30 | | - } |
31 | | - } |
| 26 | + wfResetOutputBuffers(); |
32 | 27 | |
33 | 28 | $type = wfGetType( $fname ); |
34 | 29 | if ( $type and $type!="unknown/unknown") { |
Index: trunk/phase3/includes/SpecialExport.php |
— | — | @@ -83,12 +83,7 @@ |
84 | 84 | |
85 | 85 | // Cancel output buffering and gzipping if set |
86 | 86 | // This should provide safer streaming for pages with history |
87 | | - while( $status = ob_get_status() ) { |
88 | | - ob_end_clean(); |
89 | | - if( $status['name'] == 'ob_gzhandler' ) { |
90 | | - header( 'Content-Encoding:' ); |
91 | | - } |
92 | | - } |
| 87 | + wfResetOutputBuffers(); |
93 | 88 | header( "Content-type: application/xml; charset=utf-8" ); |
94 | 89 | $pages = explode( "\n", $page ); |
95 | 90 | |
Index: trunk/phase3/includes/GlobalFunctions.php |
— | — | @@ -1080,6 +1080,64 @@ |
1081 | 1081 | } |
1082 | 1082 | |
1083 | 1083 | /** |
| 1084 | + * Clear away any user-level output buffers, discarding contents. |
| 1085 | + * |
| 1086 | + * Suitable for 'starting afresh', for instance when streaming |
| 1087 | + * relatively large amounts of data without buffering, or wanting to |
| 1088 | + * output image files without ob_gzhandler's compression. |
| 1089 | + * |
| 1090 | + * The optional $resetGzipEncoding parameter controls suppression of |
| 1091 | + * the Content-Handler header sent by ob_gzhandler; by default it |
| 1092 | + * is left. See comments for wfClearOutputBuffers() for why it would |
| 1093 | + * be used. |
| 1094 | + * |
| 1095 | + * Note that some PHP configuration options may add output buffer |
| 1096 | + * layers which cannot be removed; these are left in place. |
| 1097 | + * |
| 1098 | + * @parameter bool $resetGzipEncoding |
| 1099 | + */ |
| 1100 | +function wfResetOutputBuffers( $resetGzipEncoding=true ) { |
| 1101 | + while( $status = ob_get_status() ) { |
| 1102 | + if( $status['type'] == 0 /* PHP_OUTPUT_HANDLER_INTERNAL */ ) { |
| 1103 | + // Probably from zlib.output_compression or other |
| 1104 | + // PHP-internal setting which can't be removed. |
| 1105 | + // |
| 1106 | + // Give up, and hope the result doesn't break |
| 1107 | + // output behavior. |
| 1108 | + break; |
| 1109 | + } |
| 1110 | + if( !ob_end_clean() ) { |
| 1111 | + // Could not remove output buffer handler; abort now |
| 1112 | + // to avoid getting in some kind of infinite loop. |
| 1113 | + break; |
| 1114 | + } |
| 1115 | + if( $resetGzipEncoding ) { |
| 1116 | + if( $status['name'] == 'ob_gzhandler' ) { |
| 1117 | + // Reset the 'Content-Encoding' field set by this handler |
| 1118 | + // so we can start fresh. |
| 1119 | + header( 'Content-Encoding:' ); |
| 1120 | + } |
| 1121 | + } |
| 1122 | + } |
| 1123 | +} |
| 1124 | + |
| 1125 | +/** |
| 1126 | + * More legible than passing a 'false' parameter to wfResetOutputBuffers(): |
| 1127 | + * |
| 1128 | + * Clear away output buffers, but keep the Content-Encoding header |
| 1129 | + * produced by ob_gzhandler, if any. |
| 1130 | + * |
| 1131 | + * This should be used for HTTP 304 responses, where you need to |
| 1132 | + * preserve the Content-Encoding header of the real result, but |
| 1133 | + * also need to suppress the output of ob_gzhandler to keep to spec |
| 1134 | + * and avoid breaking Firefox in rare cases where the headers and |
| 1135 | + * body are broken over two packets. |
| 1136 | + */ |
| 1137 | +function wfClearOutputBuffers() { |
| 1138 | + wfResetOutputBuffers( false ); |
| 1139 | +} |
| 1140 | + |
| 1141 | +/** |
1084 | 1142 | * Converts an Accept-* header into an array mapping string values to quality |
1085 | 1143 | * factors |
1086 | 1144 | */ |