Commit d20e4571 authored by Dries's avatar Dries

- Patch #201122 by c960657, Moshe Weitzman: never write anonymous sessions,...

- Patch #201122 by c960657, Moshe Weitzman: never write anonymous sessions, unless something has been written to . This is an important performance improvements -- as long as you use modules that use  carefully. It might be good to report some analytics about this in the performance settings page so administrators can see if there is a 'broken' module.
parent 8cef09a7
...@@ -34,6 +34,11 @@ function _batch_page() { ...@@ -34,6 +34,11 @@ function _batch_page() {
':token' => drupal_get_token($_REQUEST['id'])) ':token' => drupal_get_token($_REQUEST['id']))
)->fetchField(); )->fetchField();
if (!$batch) {
drupal_set_message(t('No active batch.'), 'error');
drupal_goto();
}
$batch = unserialize($batch); $batch = unserialize($batch);
// Register database update for the end of processing. // Register database update for the end of processing.
...@@ -409,7 +414,7 @@ function _batch_finished() { ...@@ -409,7 +414,7 @@ function _batch_finished() {
// We get here if $form['#redirect'] was FALSE, or if the form is a // We get here if $form['#redirect'] was FALSE, or if the form is a
// multi-step form. We save the final $form_state value to be retrieved // multi-step form. We save the final $form_state value to be retrieved
// by drupal_get_form(), and redirect to the originating page. // by drupal_get_form(), and redirect to the originating page.
$_SESSION['batch_form_state'] = $_batch['form_state']; drupal_set_session('batch_form_state', $_batch['form_state']);
drupal_goto($_batch['source_page']); drupal_goto($_batch['source_page']);
} }
} }
......
...@@ -671,23 +671,41 @@ function variable_del($name) { ...@@ -671,23 +671,41 @@ function variable_del($name) {
/** /**
* Retrieve the current page from the cache. * Retrieve the current page from the cache.
* *
* Note: we do not serve cached pages when status messages are waiting (from * Note: we do not serve cached pages to authenticated users, or to anonymous
* a redirected form submission which was completed). * users when $_SESSION is non-empty. $_SESSION may contain status messages
* from a form submission, the contents of a shopping cart, or other user-
* specific content that should not be cached and displayed to other users.
*
* @param $retrieve
* If TRUE, look up and return the current page in the cache, or start output
* buffering if the conditions for caching are satisfied. If FALSE, only
* return a boolean value indicating whether the current request may be
* cached.
* @return
* The cache object, if the page was found in the cache; TRUE if the page was
* not found, but output buffering was started in order to possibly cache the
* current request; FALSE if the page was not found, and the current request
* may not be cached (e.g. because it belongs to an authenticated user). If
* $retrieve is TRUE, only return either TRUE or FALSE.
*/ */
function page_get_cache() { function page_get_cache($retrieve) {
global $user, $base_root; global $user, $base_root;
static $ob_started = FALSE;
$cache = NULL; if ($user->uid || ($_SERVER['REQUEST_METHOD'] != 'GET' && $_SERVER['REQUEST_METHOD'] != 'HEAD') || count(drupal_get_messages(NULL, FALSE))) {
return FALSE;
if (!$user->uid && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') && count(drupal_set_message()) == 0) { }
if ($retrieve) {
$cache = cache_get($base_root . request_uri(), 'cache_page'); $cache = cache_get($base_root . request_uri(), 'cache_page');
if ($cache) {
if (empty($cache)) { return $cache;
}
else {
ob_start(); ob_start();
$ob_started = TRUE;
} }
} }
return $ob_started;
return $cache;
} }
/** /**
...@@ -956,7 +974,7 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO ...@@ -956,7 +974,7 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO
function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) { function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) {
if ($message) { if ($message) {
if (!isset($_SESSION['messages'])) { if (!isset($_SESSION['messages'])) {
$_SESSION['messages'] = array(); drupal_set_session('messages', array());
} }
if (!isset($_SESSION['messages'][$type])) { if (!isset($_SESSION['messages'][$type])) {
...@@ -1093,7 +1111,7 @@ function drupal_get_bootstrap_phase() { ...@@ -1093,7 +1111,7 @@ function drupal_get_bootstrap_phase() {
} }
function _drupal_bootstrap($phase) { function _drupal_bootstrap($phase) {
global $conf; global $conf, $user;
switch ($phase) { switch ($phase) {
...@@ -1139,7 +1157,16 @@ function _drupal_bootstrap($phase) { ...@@ -1139,7 +1157,16 @@ function _drupal_bootstrap($phase) {
case DRUPAL_BOOTSTRAP_SESSION: case DRUPAL_BOOTSTRAP_SESSION:
require_once DRUPAL_ROOT . '/' . variable_get('session_inc', 'includes/session.inc'); require_once DRUPAL_ROOT . '/' . variable_get('session_inc', 'includes/session.inc');
session_set_save_handler('_sess_open', '_sess_close', '_sess_read', '_sess_write', '_sess_destroy_sid', '_sess_gc'); session_set_save_handler('_sess_open', '_sess_close', '_sess_read', '_sess_write', '_sess_destroy_sid', '_sess_gc');
session_start(); // If a session cookie exists, initialize the session. Otherwise the
// session is only started on demand in drupal_session_start(), making
// anonymous users not use a session cookie unless something is stored in
// $_SESSION. This allows HTTP proxies to cache anonymous pageviews.
if (isset($_COOKIE[session_name()])) {
drupal_session_start();
}
else {
$user = drupal_anonymous_user();
}
break; break;
case DRUPAL_BOOTSTRAP_VARIABLES: case DRUPAL_BOOTSTRAP_VARIABLES:
...@@ -1150,15 +1177,19 @@ function _drupal_bootstrap($phase) { ...@@ -1150,15 +1177,19 @@ function _drupal_bootstrap($phase) {
case DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE: case DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE:
$cache_mode = variable_get('cache', CACHE_DISABLED); $cache_mode = variable_get('cache', CACHE_DISABLED);
// Get the page from the cache. // Get the page from the cache.
$cache = $cache_mode == CACHE_DISABLED ? '' : page_get_cache(); $cache = $cache_mode == CACHE_DISABLED ? FALSE : page_get_cache(TRUE);
// If the skipping of the bootstrap hooks is not enforced, call hook_boot. // If the skipping of the bootstrap hooks is not enforced, call hook_boot.
if (!$cache || $cache_mode != CACHE_AGGRESSIVE) { if (!is_object($cache) || $cache_mode != CACHE_AGGRESSIVE) {
// Load module handling. // Load module handling.
require_once DRUPAL_ROOT . '/includes/module.inc'; require_once DRUPAL_ROOT . '/includes/module.inc';
module_invoke_all('boot'); module_invoke_all('boot');
} }
// If there is a cached page, display it. // If there is a cached page, display it.
if ($cache) { if (is_object($cache)) {
// Destroy empty anonymous sessions.
if (drupal_session_is_started() && empty($_SESSION)) {
session_destroy();
}
drupal_page_cache_header($cache); drupal_page_cache_header($cache);
// If the skipping of the bootstrap hooks is not enforced, call hook_exit. // If the skipping of the bootstrap hooks is not enforced, call hook_exit.
if ($cache_mode != CACHE_AGGRESSIVE) { if ($cache_mode != CACHE_AGGRESSIVE) {
...@@ -1169,6 +1200,13 @@ function _drupal_bootstrap($phase) { ...@@ -1169,6 +1200,13 @@ function _drupal_bootstrap($phase) {
} }
// Prepare for non-cached page workflow. // Prepare for non-cached page workflow.
drupal_page_header(); drupal_page_header();
// If the session has not already been started and output buffering is
// not enabled, the session must be started now before the HTTP headers
// are sent. If output buffering is enabled, the session may be started
// at any time using drupal_session_start().
if ($cache === FALSE) {
drupal_session_start();
}
break; break;
case DRUPAL_BOOTSTRAP_LANGUAGE: case DRUPAL_BOOTSTRAP_LANGUAGE:
......
...@@ -339,9 +339,11 @@ function drupal_goto($path = '', $query = NULL, $fragment = NULL, $http_response ...@@ -339,9 +339,11 @@ function drupal_goto($path = '', $query = NULL, $fragment = NULL, $http_response
module_invoke_all('exit', $url); module_invoke_all('exit', $url);
} }
// Even though session_write_close() is registered as a shutdown function, we if (drupal_session_is_started()) {
// need all session data written to the database before redirecting. // Even though session_write_close() is registered as a shutdown function,
session_write_close(); // we need all session data written to the database before redirecting.
session_write_close();
}
header('Location: ' . $url, TRUE, $http_response_code); header('Location: ' . $url, TRUE, $http_response_code);
...@@ -1831,6 +1833,15 @@ function l($text, $path, array $options = array()) { ...@@ -1831,6 +1833,15 @@ function l($text, $path, array $options = array()) {
* react to the closing of the page by calling hook_exit(). * react to the closing of the page by calling hook_exit().
*/ */
function drupal_page_footer() { function drupal_page_footer() {
global $user;
// Destroy empty anonymous sessions if possible.
if (!headers_sent() && drupal_session_is_started() && empty($_SESSION) && !$user->uid) {
session_destroy();
}
elseif (!empty($_SESSION) && !drupal_session_is_started()) {
watchdog('session', '$_SESSION is non-empty yet no code has called drupal_session_start().', array(), WATCHDOG_NOTICE);
}
if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED) { if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED) {
page_set_cache(); page_set_cache();
...@@ -2971,26 +2982,24 @@ function _drupal_bootstrap_full() { ...@@ -2971,26 +2982,24 @@ function _drupal_bootstrap_full() {
function page_set_cache() { function page_set_cache() {
global $user, $base_root; global $user, $base_root;
if (!$user->uid && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') && count(drupal_get_messages(NULL, FALSE)) == 0) { if (page_get_cache(FALSE)) {
// This will fail in some cases, see page_get_cache() for the explanation. $cache = TRUE;
if ($data = ob_get_contents()) { $data = ob_get_contents();
$cache = TRUE; if (variable_get('page_compression', TRUE) && function_exists('gzencode')) {
if (variable_get('page_compression', TRUE) && function_exists('gzencode')) { // We do not store the data in case the zlib mode is deflate. This should
// We do not store the data in case the zlib mode is deflate. // be rarely happening.
// This should be rarely happening. if (zlib_get_coding_type() == 'deflate') {
if (zlib_get_coding_type() == 'deflate') { $cache = FALSE;
$cache = FALSE;
}
elseif (zlib_get_coding_type() == FALSE) {
$data = gzencode($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.
} }
ob_end_flush(); elseif (zlib_get_coding_type() == FALSE) {
if ($cache && $data) { $data = gzencode($data, 9, FORCE_GZIP);
cache_set($base_root . request_uri(), $data, 'cache_page', CACHE_TEMPORARY, drupal_get_headers());
} }
// The remaining case is 'gzip' which means the data is already
// compressed and nothing left to do but to store it.
}
ob_end_flush();
if ($cache && $data) {
cache_set($base_root . request_uri(), $data, 'cache_page', CACHE_TEMPORARY, drupal_get_headers());
} }
} }
} }
......
...@@ -2476,7 +2476,7 @@ function form_clean_id($id = NULL, $flush = FALSE) { ...@@ -2476,7 +2476,7 @@ function form_clean_id($id = NULL, $flush = FALSE) {
* foreach ($results as $result) { * foreach ($results as $result) {
* $items[] = t('Loaded node %title.', array('%title' => $result)); * $items[] = t('Loaded node %title.', array('%title' => $result));
* } * }
* $_SESSION['my_batch_results'] = $items; * drupal_set_session('my_batch_results', $items);
* } * }
* @endcode * @endcode
*/ */
......
...@@ -124,11 +124,10 @@ function _sess_read($key) { ...@@ -124,11 +124,10 @@ function _sess_read($key) {
function _sess_write($key, $value) { function _sess_write($key, $value) {
global $user; global $user;
// If saving of session data is disabled or if the client doesn't have a session, // If saving of session data is disabled, or if a new empty anonymous session
// and one isn't being created ($value), do nothing. This keeps crawlers out of // has been started, do nothing. This keeps anonymous users, including
// the session table. This reduces memory and server load, and gives more useful // crawlers, out of the session table, unless they actually have something
// statistics. We can't eliminate anonymous session table rows without breaking // stored in $_SESSION.
// the "Who's Online" block.
if (!drupal_save_session() || ($user->uid == 0 && empty($_COOKIE[session_name()]) && empty($value))) { if (!drupal_save_session() || ($user->uid == 0 && empty($_COOKIE[session_name()]) && empty($value))) {
return TRUE; return TRUE;
} }
...@@ -158,6 +157,72 @@ function _sess_write($key, $value) { ...@@ -158,6 +157,72 @@ function _sess_write($key, $value) {
return TRUE; return TRUE;
} }
/**
* Propagate $_SESSION and set session cookie if not already set. This function
* should be called before writing to $_SESSION, usually via
* drupal_set_session().
*
* @param $start
* If FALSE, the session is not actually started. This is only used by
* drupal_session_is_started().
* @return
* TRUE if session has already been started, or FALSE if it has not.
*/
function drupal_session_start($start = TRUE) {
static $started = FALSE;
if ($start && !$started) {
$started = TRUE;
session_start();
}
return $started;
}
/**
* Return whether a session has been started and the $_SESSION variable is
* available.
*/
function drupal_session_is_started() {
return drupal_session_start(FALSE);
}
/**
* Get a session variable.
*
* @param $name
* The name of the variable to get. If not supplied, all variables are returned.
* @return
* The value of the variable, or FALSE if the variable is not set.
*/
function drupal_get_session($name = NULL) {
if (is_null($name)) {
return $_SESSION;
}
elseif (isset($_SESSION[$name])) {
return $_SESSION[$name];
}
else {
return FALSE;
}
}
/**
* Set a session variable. The variable becomes accessible via $_SESSION[$name]
* in the current and later requests. If there is no active PHP session prior
* to the call, one is started automatically.
*
* Anonymous users generate less server load if their $_SESSION variable is
* empty, so unused entries should be unset using unset($_SESSION['foo']).
*
* @param $name
* The name of the variable to set.
* @param $value
* The value to set.
*/
function drupal_set_session($name, $value) {
drupal_session_start();
$_SESSION[$name] = $value;
}
/** /**
* Called when an anonymous user becomes authenticated or vice-versa. * Called when an anonymous user becomes authenticated or vice-versa.
*/ */
...@@ -211,6 +276,9 @@ function _sess_destroy_sid($sid) { ...@@ -211,6 +276,9 @@ function _sess_destroy_sid($sid) {
db_delete('sessions') db_delete('sessions')
->condition('sid', $sid) ->condition('sid', $sid)
->execute(); ->execute();
// Unset cookie.
extract(session_get_cookie_params());
setcookie(session_name(), '', time() - 3600, $path, $domain, $secure, $httponly);
} }
/** /**
......
...@@ -628,7 +628,7 @@ function install_tasks($profile, $task) { ...@@ -628,7 +628,7 @@ function install_tasks($profile, $task) {
drupal_install_init_database(); drupal_install_init_database();
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
$_SESSION['messages'] = $messages; drupal_set_session('messages', $messages);
// URL used to direct page requests. // URL used to direct page requests.
$url = $base_url . '/install.php?locale=' . $install_locale . '&profile=' . $profile; $url = $base_url . '/install.php?locale=' . $install_locale . '&profile=' . $profile;
......
...@@ -129,8 +129,8 @@ function book_update_6000() { ...@@ -129,8 +129,8 @@ function book_update_6000() {
db_create_table($ret, 'book', $schema['book']); db_create_table($ret, 'book', $schema['book']);
$_SESSION['book_update_6000_orphans']['from'] = 0; drupal_set_session('book_update_6000_orphans', array('from' => 0));
$_SESSION['book_update_6000'] = array(); drupal_set_session('book_update_6000', array());
$result = db_query("SELECT * from {book_temp} WHERE parent = 0"); $result = db_query("SELECT * from {book_temp} WHERE parent = 0");
// Collect all books - top-level nodes. // Collect all books - top-level nodes.
......
...@@ -262,8 +262,10 @@ function _dblog_format_message($dblog) { ...@@ -262,8 +262,10 @@ function _dblog_format_message($dblog) {
* @see dblog_filter_form_validate() * @see dblog_filter_form_validate()
*/ */
function dblog_filter_form() { function dblog_filter_form() {
if (!isset($_SESSION['dblog_overview_filter'])) {
drupal_set_session('dblog_overview_filter', array());
}
$session = &$_SESSION['dblog_overview_filter']; $session = &$_SESSION['dblog_overview_filter'];
$session = is_array($session) ? $session : array();
$filters = dblog_filters(); $filters = dblog_filters();
$form['filters'] = array( $form['filters'] = array(
...@@ -319,12 +321,15 @@ function dblog_filter_form_submit($form, &$form_state) { ...@@ -319,12 +321,15 @@ function dblog_filter_form_submit($form, &$form_state) {
case t('Filter'): case t('Filter'):
foreach ($filters as $name => $filter) { foreach ($filters as $name => $filter) {
if (isset($form_state['values'][$name])) { if (isset($form_state['values'][$name])) {
if (!isset($_SESSION['dblog_overview_filter'])) {
drupal_set_session('dblog_overview_filter', array());
}
$_SESSION['dblog_overview_filter'][$name] = $form_state['values'][$name]; $_SESSION['dblog_overview_filter'][$name] = $form_state['values'][$name];
} }
} }
break; break;
case t('Reset'): case t('Reset'):
$_SESSION['dblog_overview_filter'] = array(); drupal_set_session('dblog_overview_filter', array());
break; break;
} }
return 'admin/reports/dblog'; return 'admin/reports/dblog';
......
...@@ -204,8 +204,10 @@ function node_build_filter_query() { ...@@ -204,8 +204,10 @@ function node_build_filter_query() {
* Return form for node administration filters. * Return form for node administration filters.
*/ */
function node_filter_form() { function node_filter_form() {
if (!isset($_SESSION['node_overview_filter'])) {
drupal_set_session('node_overview_filter', array());
}
$session = &$_SESSION['node_overview_filter']; $session = &$_SESSION['node_overview_filter'];
$session = is_array($session) ? $session : array();
$filters = node_filters(); $filters = node_filters();
$i = 0; $i = 0;
...@@ -320,6 +322,9 @@ function node_filter_form_submit($form, &$form_state) { ...@@ -320,6 +322,9 @@ function node_filter_form_submit($form, &$form_state) {
$flat_options = form_options_flatten($filters[$filter]['options']); $flat_options = form_options_flatten($filters[$filter]['options']);
if (isset($flat_options[$form_state['values'][$filter]])) { if (isset($flat_options[$form_state['values'][$filter]])) {
if (!isset($_SESSION['node_overview_filter'])) {
drupal_set_session('node_overview_filter', array());
}
$_SESSION['node_overview_filter'][] = array($filter, $form_state['values'][$filter]); $_SESSION['node_overview_filter'][] = array($filter, $form_state['values'][$filter]);
} }
} }
...@@ -328,7 +333,7 @@ function node_filter_form_submit($form, &$form_state) { ...@@ -328,7 +333,7 @@ function node_filter_form_submit($form, &$form_state) {
array_pop($_SESSION['node_overview_filter']); array_pop($_SESSION['node_overview_filter']);
break; break;
case t('Reset'): case t('Reset'):
$_SESSION['node_overview_filter'] = array(); drupal_set_session('node_overview_filter', array());
break; break;
} }
} }
......
...@@ -161,6 +161,9 @@ function openid_begin($claimed_id, $return_to = '', $form_values = array()) { ...@@ -161,6 +161,9 @@ function openid_begin($claimed_id, $return_to = '', $form_values = array()) {
} }
// Store discovered information in the users' session so we don't have to rediscover. // Store discovered information in the users' session so we don't have to rediscover.
if (!isset($_SESSION['openid'])) {
drupal_set_session('openid', array());
}
$_SESSION['openid']['service'] = $services[0]; $_SESSION['openid']['service'] = $services[0];
// Store the claimed id // Store the claimed id
$_SESSION['openid']['claimed_id'] = $claimed_id; $_SESSION['openid']['claimed_id'] = $claimed_id;
...@@ -408,6 +411,9 @@ function openid_authentication($response) { ...@@ -408,6 +411,9 @@ function openid_authentication($response) {
// We were unable to register a valid new user, redirect to standard // We were unable to register a valid new user, redirect to standard
// user/register and prefill with the values we received. // user/register and prefill with the values we received.
drupal_set_message(t('OpenID registration failed for the reasons listed. You may register now, or if you already have an account you can <a href="@login">log in</a> now and add your OpenID under "My Account"', array('@login' => url('user/login'))), 'error'); drupal_set_message(t('OpenID registration failed for the reasons listed. You may register now, or if you already have an account you can <a href="@login">log in</a> now and add your OpenID under "My Account"', array('@login' => url('user/login'))), 'error');
if (!isset($_SESSION['openid'])) {
drupal_set_session('openid', array());
}
$_SESSION['openid']['values'] = $form_state['values']; $_SESSION['openid']['values'] = $form_state['values'];
// We'll want to redirect back to the same place. // We'll want to redirect back to the same place.
$destination = drupal_get_destination(); $destination = drupal_get_destination();
......
...@@ -919,8 +919,9 @@ protected function tearDown() { ...@@ -919,8 +919,9 @@ protected function tearDown() {
$user = $this->originalUser; $user = $this->originalUser;
drupal_save_session(TRUE); drupal_save_session(TRUE);
// Ensure that the internal logged in variable is reset. // Ensure that internal logged in variable and cURL options are reset.
$this->isLoggedIn = FALSE; $this->isLoggedIn = FALSE;
$this->additionalCurlOptions = array();
// Reload module list and implementations to ensure that test module hooks // Reload module list and implementations to ensure that test module hooks
// aren't called after tests. // aren't called after tests.
......
...@@ -436,7 +436,7 @@ function _simpletest_batch_operation($test_list_init, $test_id, &$context) { ...@@ -436,7 +436,7 @@ function _simpletest_batch_operation($test_list_init, $test_id, &$context) {
function _simpletest_batch_finished($success, $results, $operations, $elapsed) { function _simpletest_batch_finished($success, $results, $operations, $elapsed) {
if (isset($results['test_id'])) { if (isset($results['test_id'])) {
$_SESSION['test_id'] = $results['test_id']; drupal_set_session('test_id', $results['test_id']);
} }
if ($success) { if ($success) {
drupal_set_message(t('The tests finished in @elapsed.', array('@elapsed' => $elapsed))); drupal_set_message(t('The tests finished in @elapsed.', array('@elapsed' => $elapsed)));
......
...@@ -145,6 +145,89 @@ class SessionTestCase extends DrupalWebTestCase { ...@@ -145,6 +145,89 @@ class SessionTestCase extends DrupalWebTestCase {
} }
/**
* Test that empty anonymous sessions are destroyed.
*/
function testEmptyAnonymousSession() {
// With caching disabled, a session is always started.
$this->drupalGet('');
$this->assertSessionCookie(FALSE);
$this->assertSessionStarted(TRUE);
$this->assertSessionEmpty(TRUE);
variable_set('cache', CACHE_NORMAL);
// During this request the session is destroyed in drupal_page_footer(),
// and the session cookie is unset.
$this->drupalGet('');
$this->assertSessionCookie(TRUE);
$this->assertSessionStarted(TRUE);
$this->assertSessionEmpty(TRUE);
$this->assertFalse($this->drupalGetHeader('ETag'), t('Page was not cached.'));
// When PHP deletes a cookie, it sends "Set-Cookie: cookiename=deleted;
// expires=..."
$this->assertTrue(preg_match('/SESS\w+=deleted/', $this->drupalGetHeader('Set-Cookie')), t('Session cookie was deleted.'));
// Verify that the session cookie was actually deleted.
$this->drupalGet('');
$this->assertSessionCookie(FALSE);
$this->assertSessionStarted(FALSE);
$this->assertFalse($this->drupalGetHeader('Set-Cookie'), t('New session was not started.'));
// Start a new session by setting a message.
$this->drupalGet('session-test/set-message');
$this->assertSessionCookie(FALSE);
$this->assertSessionStarted(FALSE);
$this->assertTrue($this->drupalGetHeader('Set-Cookie'), t('New session was started.'));
// Display the message.
$this->drupalGet('');
$this->assertSessionCookie(TRUE);
$this->assertSessionStarted(TRUE);
$this->assertSessionEmpty(FALSE);
$this->assertFalse($this->drupalGetHeader('ETag'), t('Page was not cached.'));
$this->assertText(t('This is a dummy message.'), t('Message was displayed.'));
// During this request the session is destroyed in _drupal_bootstrap(),
// and the session cookie is unset.
$this->drupalGet('');
$this->assertSessionCookie(TRUE);
$this->assertSessionStarted(TRUE);
$this->assertSessionEmpty(TRUE);
$this->assertTrue($this->drupalGetHeader('ETag'), t('Page was cached.'));
$this->assertNoText(t('This is a dummy message.'), t('Message was not cached.'));
$this->assertTrue(preg_match('/SESS\w+=deleted/', $this->drupalGetHeader('Set-Cookie')), t('Session cookie was deleted.'));
// Verify that session was destroyed.
$this->drupalGet('');
$this->assertSessionCookie(FALSE);
$this->assertSessionStarted(FALSE);
$this->assertTrue($this->drupalGetHeader('ETag'), t('Page was cached.'));
$this->assertFalse($this->drupalGetHeader('Set-Cookie'), t('New session was not started.'));
// Verify that modifying $_SESSION without having started a session
// generates a watchdog message, and that no messages have been generated
// so far.
$this->assertEqual($this->getWarningCount(), 0, t('No watchdog messages have been generated'));
$this->drupalGet('/session-test/set-not-started');
$this->assertSessionCookie(FALSE);
$this->assertSessionStarted(FALSE);
$this->assertEqual($this->getWarningCount(), 1, t('1 watchdog messages has been generated'));
}
/**
* Count watchdog messages about modifying $_SESSION without having started a
* session.
*/
function getWarningCount() {
return db_select('watchdog')
->condition('type', 'session')
->condition('message', '$_SESSION is non-empty yet no code has called drupal_session_start().')
->countQuery()
->execute()
->fetchField();
}
/** /**
* Reset the cookie file so that it refers to the specified user. * Reset the cookie file so that it refers to the specified user.
* *
...@@ -161,4 +244,40 @@ class SessionTestCase extends DrupalWebTestCase { ...@@ -161,4 +244,40 @@ class SessionTestCase extends DrupalWebTestCase {