Index: includes/bootstrap.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v retrieving revision 1.277 diff -u -9 -p -r1.277 bootstrap.inc --- includes/bootstrap.inc 24 Apr 2009 08:15:50 -0000 1.277 +++ includes/bootstrap.inc 26 Apr 2009 21:22:03 -0000 @@ -985,18 +985,21 @@ function drupal_page_cache_header(stdCla // that the module knows how to cache the page. if (!isset($hook_boot_headers['vary']) && !variable_get('omit_vary_cookie')) { header('Vary: Cookie'); } if ($page_compression) { header('Vary: Accept-Encoding', FALSE); // If page_compression is enabled, the cache contains gzipped data. if ($return_compressed) { + // $cache->data is already gzip'ed, so make sure zlib.output_compression + // does not compress it once more. + ini_set('zlib.output_compression', '0'); header('Content-Encoding: gzip'); } else { // The client does not support compression, so unzip the data in the // cache. Strip the gzip header and run uncompress. $cache->data = gzinflate(substr(substr($cache->data, 10), 0, -8)); } } Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.887 diff -u -9 -p -r1.887 common.inc --- includes/common.inc 26 Apr 2009 19:44:37 -0000 1.887 +++ includes/common.inc 26 Apr 2009 21:22:03 -0000 @@ -3024,59 +3024,49 @@ function _drupal_bootstrap_full() { // We do not want this while running update.php. if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') { module_invoke_all('init'); } } /** * Store the current page in the cache. * - * We try to store a gzipped version of the cache. This requires the - * PHP zlib extension (http://php.net/manual/en/ref.zlib.php). - * Presence of the extension is checked by testing for the function - * gzencode. There are two compression algorithms: gzip and deflate. - * The majority of all modern browsers support gzip or both of them. - * We thus only deal with the gzip variant and unzip the cache in case - * the browser does not accept gzip encoding. + * If page_compression is enabled, a gzipped version of the page is stored in + * the cache to avoid compressing the output on each request. The cache entry + * is unzipped in the relatively rare event that the page is requested by a + * client without gzip support. + * + * Page compression requires the PHP zlib extension + * (http://php.net/manual/en/ref.zlib.php). * * @see drupal_page_header */ function page_set_cache() { - global $user, $base_root; + global $base_root; if (page_get_cache(FALSE)) { - $cache_page = TRUE; $cache = (object) array( 'cid' => $base_root . request_uri(), 'data' => ob_get_clean(), 'expire' => CACHE_TEMPORARY, 'created' => REQUEST_TIME, 'headers' => array(), ); // Restore preferred header names based on the lower-case names returned // by drupal_get_header(). $header_names = _drupal_set_preferred_header_name(); foreach (drupal_get_header() as $name_lower => $value) { $cache->headers[$header_names[$name_lower]] = $value; } - if (variable_get('page_compression', TRUE) && function_exists('gzencode')) { - // We do not store the data in case the zlib mode is deflate. This should - // be rarely happening. - if (zlib_get_coding_type() == 'deflate') { - $cache_page = FALSE; - } - elseif (zlib_get_coding_type() == FALSE) { + if ($cache->data) { + if (variable_get('page_compression', TRUE) && extension_loaded('zlib')) { $cache->data = gzencode($cache->data, 9, FORCE_GZIP); } - // The remaining case is 'gzip' which means the data is already - // compressed and nothing left to do but to store it. - } - if ($cache_page && $cache->data) { cache_set($cache->cid, $cache->data, 'cache_page', $cache->expire, $cache->headers); } drupal_page_cache_header($cache); } else { // If output buffering was enabled during bootstrap, and the headers were // not sent in the DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE phase, send them now. drupal_page_header(); } Index: modules/simpletest/drupal_web_test_case.php =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/drupal_web_test_case.php,v retrieving revision 1.98 diff -u -9 -p -r1.98 drupal_web_test_case.php --- modules/simpletest/drupal_web_test_case.php 25 Apr 2009 22:35:49 -0000 1.98 +++ modules/simpletest/drupal_web_test_case.php 26 Apr 2009 21:22:04 -0000 @@ -1215,19 +1215,19 @@ class DrupalWebTestCase { /** * Check for meta refresh tag and if found call drupalGet() recursively. This * function looks for the http-equiv attribute to be set to "Refresh" * and is case-sensitive. * * @return * Either the new page content or FALSE. */ protected function checkForMetaRefresh() { - if ($this->drupalGetContent() != '' && $this->parse()) { + if (strpos($this->drupalGetContent(), 'parse()) { $refresh = $this->xpath('//meta[@http-equiv="Refresh"]'); if (!empty($refresh)) { // Parse the content attribute of the meta tag for the format: // "[delay]: URL=[page_to_redirect_to]". if (preg_match('/\d+;\s*URL=(?P.*)/i', $refresh[0]['content'], $match)) { return $this->drupalGet($this->getAbsoluteUrl(decode_entities($match['url']))); } } } Index: modules/simpletest/tests/bootstrap.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/bootstrap.test,v retrieving revision 1.15 diff -u -9 -p -r1.15 bootstrap.test --- modules/simpletest/tests/bootstrap.test 22 Apr 2009 09:45:03 -0000 1.15 +++ modules/simpletest/tests/bootstrap.test 26 Apr 2009 21:22:04 -0000 @@ -85,19 +85,19 @@ class BootstrapIPAddressTestCase extends $this->assertTrue(drupal_valid_http_host('[::1]:80'), t('HTTP_HOST containing IPv6 loopback is valid')); } } class BootstrapPageCacheTestCase extends DrupalWebTestCase { public static function getInfo() { return array( 'name' => t('Page cache test'), - 'description' => t('Enable the page cache and test it with conditional HTTP requests.'), + 'description' => t('Enable the page cache and test it with various HTTP requests.'), 'group' => t('Bootstrap') ); } function setUp() { parent::setUp('system_test'); } /** @@ -171,18 +171,81 @@ class BootstrapPageCacheTestCase extends $this->drupalLogin($user); $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar'))); $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), t('Caching was bypassed.')); $this->assertFalse($this->drupalGetHeader('Vary'), t('Vary header was not sent.')); $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'no-cache, must-revalidate, post-check=0, pre-check=0', t('Cache-Control header was sent.')); $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', t('Expires header was sent.')); $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', t('Custom header was sent.')); } + + /** + * Test page compression when zlib.output_compression is disabled. + * + * The test should pass even if zlib.output_compression is enabled in php.ini, + * .htaccess or similar, or if compression is done outside PHP, e.g. by the + * mod_deflate Apache module. + */ + function testPageCompression() { + variable_set('cache', CACHE_NORMAL); + // Disable zlib.output_compression in system_test_boot(). + variable_set('zlib_output_compression', 'O'); + + // Fill the cache. The response is not compressed, unless compression is + // done outside PHP, e.g. by the mod_deflate Apache module. + $this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate')); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', t('Page was cached.')); + + // Verify that cached output is compressed. + $this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate')); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.')); + $this->assertEqual($this->drupalGetHeader('Content-Encoding'), 'gzip', t('A Content-Encoding header was sent.')); + $this->assertTrue(strpos(gzinflate(substr($this->drupalGetContent(), 10, -8)), t('Welcome to your new Drupal website!')), t('Page was gzip compressed.')); + + // Verify that a client with compression support gets an uncompressed page. + $this->drupalGet(''); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.')); + $this->assertFalse($this->drupalGetHeader('Content-Encoding'), t('A Content-Encoding header was not sent.')); + $this->assertText(t('Welcome to your new Drupal website!'), t('Page was not compressed.')); + + // Verify that a client without gzip support gets an uncompressed page. + $this->drupalGet('', array(), array('Accept-Encoding: deflate')); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.')); + $this->assertFalse($this->drupalGetHeader('Content-Encoding'), t('A Content-Encoding header was not sent.')); + $this->assertText(t('Welcome to your new Drupal website!'), t('Page was not compressed.')); + } + + /** + * Test page compression when zlib.output_compression is enabled. + * + * Requires that zlib.output_compression is enabled in php.ini, .htaccess or + * similar. + */ + function testPageCompressionWithZlibOutputCompression() { + // zlib.output_compression cannot be enabled at runtime, so this test can + // only run, if it has been enabled in php.ini, .htaccess or similar. See + // http://bugs.php.net/bug.php?id=33653 + if (!ini_get('zlib.output_compression')) { + return; + } + + variable_set('cache', CACHE_NORMAL); + + // Fill the cache. Compression is done by zlib.output_compression. + $this->drupalGet('', array(), array('Accept-Encoding: deflate')); + $this->assertEqual($this->drupalGetHeader('Content-Encoding'), 'deflate', t('A Content-Encoding header was sent.')); + + // Verify that output is gzip'ed, even though the cache was filled when + // zlib.output_compression was doing deflate compression. + $this->drupalGet('', array(), array('Accept-Encoding: gzip')); + $this->assertEqual($this->drupalGetHeader('Content-Encoding'), 'gzip', t('Page was compressed.')); + $this->assertTrue(strpos(gzinflate(substr($this->drupalGetContent(), 10, -8)), t('Welcome to your new Drupal website!')), t('Page was compressed.')); + } } class BootstrapVariableTestCase extends DrupalWebTestCase { function setUp() { parent::setUp('system_test'); } public static function getInfo() { Index: modules/simpletest/tests/common.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v retrieving revision 1.34 diff -u -9 -p -r1.34 common.test --- modules/simpletest/tests/common.test 25 Apr 2009 13:56:06 -0000 1.34 +++ modules/simpletest/tests/common.test 26 Apr 2009 21:22:04 -0000 @@ -647,33 +647,33 @@ class DrupalErrorHandlerUnitTest extends /** * Test the error handler. */ function testErrorHandler() { $error_notice = array( '%type' => 'Notice', '%message' => 'Undefined variable: bananas', '%function' => 'system_test_generate_warnings()', - '%line' => 194, + '%line' => 199, '%file' => realpath('modules/simpletest/tests/system_test.module'), ); $error_warning = array( '%type' => 'Warning', '%message' => 'Division by zero', '%function' => 'system_test_generate_warnings()', - '%line' => 196, + '%line' => 201, '%file' => realpath('modules/simpletest/tests/system_test.module'), ); $error_user_notice = array( '%type' => 'User notice', '%message' => 'Drupal is awesome', '%function' => 'system_test_generate_warnings()', - '%line' => 198, + '%line' => 203, '%file' => realpath('modules/simpletest/tests/system_test.module'), ); // Set error reporting to collect notices. variable_set('error_level', 2); $this->drupalGet('system-test/generate-warnings'); $this->assertErrorMessage($error_notice); $this->assertErrorMessage($error_warning); $this->assertErrorMessage($error_user_notice); @@ -695,26 +695,26 @@ class DrupalErrorHandlerUnitTest extends /** * Test the exception handler. */ function testExceptionHandler() { $error_exception = array( '%type' => 'Exception', '%message' => 'Drupal is awesome', '%function' => 'system_test_trigger_exception()', - '%line' => 207, + '%line' => 212, '%file' => realpath('modules/simpletest/tests/system_test.module'), ); $error_pdo_exception = array( '%type' => 'PDOException', '%message' => 'SQLSTATE', '%function' => 'system_test_trigger_pdo_exception()', - '%line' => 215, + '%line' => 220, '%file' => realpath('modules/simpletest/tests/system_test.module'), ); $this->drupalGet('system-test/trigger-exception'); $this->assertErrorMessage($error_exception); $this->drupalGet('system-test/trigger-pdo-exception'); // We cannot use assertErrorMessage() since the extact error reported // varies from database to database. Check for the error keyword 'SQLSTATE'. Index: modules/simpletest/tests/system_test.module =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/system_test.module,v retrieving revision 1.9 diff -u -9 -p -r1.9 system_test.module --- modules/simpletest/tests/system_test.module 22 Apr 2009 09:45:03 -0000 1.9 +++ modules/simpletest/tests/system_test.module 26 Apr 2009 21:22:04 -0000 @@ -159,18 +159,23 @@ function system_test_modules_uninstalled drupal_set_message(t('hook_modules_uninstalled fired for aggregator')); } } /** * Implementation of hook_boot(). */ function system_test_boot() { watchdog('system_test', 'hook_boot'); + + $zlib_output_compression = variable_get('zlib_output_compression', NULL); + if ($zlib_output_compression !== NULL) { + ini_set('zlib.output_compression', $zlib_output_compression); + } } /** * Implementation of hook_init(). */ function system_test_init() { // Used by FrontPageTestCase to get the results of drupal_is_front_page(). if (variable_get('front_page_output', 0) && drupal_is_front_page()) { drupal_set_message(t('On front page.'));