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() {
':token' => drupal_get_token($_REQUEST['id']))
)->fetchField();
if (!$batch) {
drupal_set_message(t('No active batch.'), 'error');
drupal_goto();
}
$batch = unserialize($batch);
// Register database update for the end of processing.
......@@ -409,7 +414,7 @@ function _batch_finished() {
// 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
// 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']);
}
}
......
......@@ -671,23 +671,41 @@ function variable_del($name) {
/**
* Retrieve the current page from the cache.
*
* Note: we do not serve cached pages when status messages are waiting (from
* a redirected form submission which was completed).
* Note: we do not serve cached pages to authenticated users, or to anonymous
* 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;
static $ob_started = FALSE;
$cache = NULL;
if (!$user->uid && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') && count(drupal_set_message()) == 0) {
if ($user->uid || ($_SERVER['REQUEST_METHOD'] != 'GET' && $_SERVER['REQUEST_METHOD'] != 'HEAD') || count(drupal_get_messages(NULL, FALSE))) {
return FALSE;
}
if ($retrieve) {
$cache = cache_get($base_root . request_uri(), 'cache_page');
if (empty($cache)) {
if ($cache) {
return $cache;
}
else {
ob_start();
$ob_started = TRUE;
}
}
return $cache;
return $ob_started;
}
/**
......@@ -956,7 +974,7 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO
function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) {
if ($message) {
if (!isset($_SESSION['messages'])) {
$_SESSION['messages'] = array();
drupal_set_session('messages', array());
}
if (!isset($_SESSION['messages'][$type])) {
......@@ -1093,7 +1111,7 @@ function drupal_get_bootstrap_phase() {
}
function _drupal_bootstrap($phase) {
global $conf;
global $conf, $user;
switch ($phase) {
......@@ -1139,7 +1157,16 @@ function _drupal_bootstrap($phase) {
case DRUPAL_BOOTSTRAP_SESSION:
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_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;
case DRUPAL_BOOTSTRAP_VARIABLES:
......@@ -1150,15 +1177,19 @@ function _drupal_bootstrap($phase) {
case DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE:
$cache_mode = variable_get('cache', CACHE_DISABLED);
// 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 (!$cache || $cache_mode != CACHE_AGGRESSIVE) {
if (!is_object($cache) || $cache_mode != CACHE_AGGRESSIVE) {
// Load module handling.
require_once DRUPAL_ROOT . '/includes/module.inc';
module_invoke_all('boot');
}
// 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);
// If the skipping of the bootstrap hooks is not enforced, call hook_exit.
if ($cache_mode != CACHE_AGGRESSIVE) {
......@@ -1169,6 +1200,13 @@ function _drupal_bootstrap($phase) {
}
// Prepare for non-cached page workflow.
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;
case DRUPAL_BOOTSTRAP_LANGUAGE:
......
......@@ -339,9 +339,11 @@ function drupal_goto($path = '', $query = NULL, $fragment = NULL, $http_response
module_invoke_all('exit', $url);
}
// Even though session_write_close() is registered as a shutdown function, we
// need all session data written to the database before redirecting.
session_write_close();
if (drupal_session_is_started()) {
// Even though session_write_close() is registered as a shutdown function,
// we need all session data written to the database before redirecting.
session_write_close();
}
header('Location: ' . $url, TRUE, $http_response_code);
......@@ -1831,6 +1833,15 @@ function l($text, $path, array $options = array()) {
* react to the closing of the page by calling hook_exit().
*/
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) {
page_set_cache();
......@@ -2971,26 +2982,24 @@ function _drupal_bootstrap_full() {
function page_set_cache() {
global $user, $base_root;
if (!$user->uid && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') && count(drupal_get_messages(NULL, FALSE)) == 0) {
// This will fail in some cases, see page_get_cache() for the explanation.
if ($data = ob_get_contents()) {
$cache = TRUE;
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 = 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.
if (page_get_cache(FALSE)) {
$cache = TRUE;
$data = ob_get_contents();
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 = FALSE;
}
ob_end_flush();
if ($cache && $data) {
cache_set($base_root . request_uri(), $data, 'cache_page', CACHE_TEMPORARY, drupal_get_headers());
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();
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) {
* foreach ($results as $result) {
* $items[] = t('Loaded node %title.', array('%title' => $result));
* }
* $_SESSION['my_batch_results'] = $items;
* drupal_set_session('my_batch_results', $items);
* }
* @endcode
*/
......
......@@ -124,11 +124,10 @@ function _sess_read($key) {
function _sess_write($key, $value) {
global $user;
// If saving of session data is disabled or if the client doesn't have a session,
// and one isn't being created ($value), do nothing. This keeps crawlers out of
// the session table. This reduces memory and server load, and gives more useful
// statistics. We can't eliminate anonymous session table rows without breaking
// the "Who's Online" block.
// If saving of session data is disabled, or if a new empty anonymous session
// has been started, do nothing. This keeps anonymous users, including
// crawlers, out of the session table, unless they actually have something
// stored in $_SESSION.
if (!drupal_save_session() || ($user->uid == 0 && empty($_COOKIE[session_name()]) && empty($value))) {
return TRUE;
}
......@@ -158,6 +157,72 @@ function _sess_write($key, $value) {
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.
*/
......@@ -211,6 +276,9 @@ function _sess_destroy_sid($sid) {
db_delete('sessions')
->condition('sid', $sid)
->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) {
drupal_install_init_database();
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
$_SESSION['messages'] = $messages;
drupal_set_session('messages', $messages);
// URL used to direct page requests.
$url = $base_url . '/install.php?locale=' . $install_locale . '&profile=' . $profile;
......
......@@ -129,8 +129,8 @@ function book_update_6000() {
db_create_table($ret, 'book', $schema['book']);
$_SESSION['book_update_6000_orphans']['from'] = 0;
$_SESSION['book_update_6000'] = array();
drupal_set_session('book_update_6000_orphans', array('from' => 0));
drupal_set_session('book_update_6000', array());
$result = db_query("SELECT * from {book_temp} WHERE parent = 0");
// Collect all books - top-level nodes.
......
......@@ -262,8 +262,10 @@ function _dblog_format_message($dblog) {
* @see dblog_filter_form_validate()
*/
function dblog_filter_form() {
if (!isset($_SESSION['dblog_overview_filter'])) {
drupal_set_session('dblog_overview_filter', array());
}
$session = &$_SESSION['dblog_overview_filter'];
$session = is_array($session) ? $session : array();
$filters = dblog_filters();
$form['filters'] = array(
......@@ -319,12 +321,15 @@ function dblog_filter_form_submit($form, &$form_state) {
case t('Filter'):
foreach ($filters as $name => $filter) {
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];
}
}
break;
case t('Reset'):
$_SESSION['dblog_overview_filter'] = array();
drupal_set_session('dblog_overview_filter', array());
break;
}
return 'admin/reports/dblog';
......
......@@ -204,8 +204,10 @@ function node_build_filter_query() {
* Return form for node administration filters.
*/
function node_filter_form() {
if (!isset($_SESSION['node_overview_filter'])) {
drupal_set_session('node_overview_filter', array());
}
$session = &$_SESSION['node_overview_filter'];
$session = is_array($session) ? $session : array();
$filters = node_filters();
$i = 0;
......@@ -320,6 +322,9 @@ function node_filter_form_submit($form, &$form_state) {
$flat_options = form_options_flatten($filters[$filter]['options']);
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]);
}
}
......@@ -328,7 +333,7 @@ function node_filter_form_submit($form, &$form_state) {
array_pop($_SESSION['node_overview_filter']);
break;
case t('Reset'):
$_SESSION['node_overview_filter'] = array();
drupal_set_session('node_overview_filter', array());
break;
}
}
......
......@@ -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.
if (!isset($_SESSION['openid'])) {
drupal_set_session('openid', array());
}
$_SESSION['openid']['service'] = $services[0];
// Store the claimed id
$_SESSION['openid']['claimed_id'] = $claimed_id;
......@@ -408,6 +411,9 @@ function openid_authentication($response) {
// We were unable to register a valid new user, redirect to standard
// 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');
if (!isset($_SESSION['openid'])) {
drupal_set_session('openid', array());
}
$_SESSION['openid']['values'] = $form_state['values'];
// We'll want to redirect back to the same place.
$destination = drupal_get_destination();
......
......@@ -919,8 +919,9 @@ protected function tearDown() {
$user = $this->originalUser;
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->additionalCurlOptions = array();
// Reload module list and implementations to ensure that test module hooks
// aren't called after tests.
......
......@@ -436,7 +436,7 @@ function _simpletest_batch_operation($test_list_init, $test_id, &$context) {
function _simpletest_batch_finished($success, $results, $operations, $elapsed) {
if (isset($results['test_id'])) {
$_SESSION['test_id'] = $results['test_id'];
drupal_set_session('test_id', $results['test_id']);
}
if ($success) {
drupal_set_message(t('The tests finished in @elapsed.', array('@elapsed' => $elapsed)));
......
......@@ -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.
*
......@@ -161,4 +244,40 @@ class SessionTestCase extends DrupalWebTestCase {
$this->drupalGet('session-test/get');
$this->assertResponse(200, t('Session test module is correctly enabled.'), t('Session'));
}
/**
* Assert whether the SimpleTest browser sent a session cookie.
*/
function assertSessionCookie($sent) {
if ($sent) {
$this->assertIdentical($this->drupalGetHeader('X-Session-Cookie'), '1', t('Session cookie was sent.'));
}
else {
$this->assertIdentical($this->drupalGetHeader('X-Session-Cookie'), '0', t('Session cookie was not sent.'));
}
}
/**
* Assert whether session was started during the bootstrap process.
*/
function assertSessionStarted($started) {
if ($started) {
$this->assertIdentical($this->drupalGetHeader('X-Session-Started'), '1', t('Session was started.'));
}
else {
$this->assertIdentical($this->drupalGetHeader('X-Session-Started'), '0', t('Session was not started.'));
}
}
/**
* Assert whether $_SESSION is empty at the beginning of the request.
*/
function assertSessionEmpty($empty) {
if ($empty) {
$this->assertIdentical($this->drupalGetHeader('X-Session-Empty'), '1', t('Session was empty.'));
}
else {
$this->assertIdentical($this->drupalGetHeader('X-Session-Empty'), '0', t('Session was not empty.'));
}
}
}
<?php
// $Id$
/**
* @file
* Provides SimpleTests for core session handling functionality.
*/
class SessionTestCase extends DrupalWebTestCase {
protected $saved_cookie;
function getInfo() {
return array(
'name' => t('Session tests'),
'description' => t('Drupal session handling tests.'),
'group' => t('Session')
);
}
function setUp() {
parent::setUp('session_test');
}
/**
* Implementation of curlHeaderCallback().
*/
protected function curlHeaderCallback($ch, $header) {
// Look for a Set-Cookie header.
if (preg_match('/^Set-Cookie.+$/i', $header, $matches)) {
$this->saved_cookie = $header;
}
return parent::curlHeaderCallback($ch, $header);
}
/**
* Tests for drupal_save_session() and drupal_session_regenerate().
*/
function testSessionSaveRegenerate() {
$this->assertTrue(drupal_save_session(), t('drupal_save_session() correctly returns TRUE when initially called with no arguments.'), t('Session'));
$this->assertFalse(drupal_save_session(FALSE), t('drupal_save_session() correctly returns FALSE when called with FALSE.'), t('Session'));
$this->assertFalse(drupal_save_session(), t('drupal_save_session() correctly returns FALSE when saving has been disabled.'), t('Session'));
$this->assertTrue(drupal_save_session(TRUE), t('drupal_save_session() correctly returns TRUE when called with TRUE.'), t('Session'));
$this->assertTrue(drupal_save_session(), t('drupal_save_session() correctly returns TRUE when saving has been enabled.'), t('Session'));
// Test session hardening code from SA-2008-044.
$user = $this->drupalCreateUser(array('access content'));
// Enable sessions.
$this->sessionReset($user->uid);
// Make sure the session cookie is set as HttpOnly.
$this->drupalLogin($user);
$this->assertTrue(preg_match('/HttpOnly/i', $this->saved_cookie), t('Session cookie is set as HttpOnly.'));
$this->drupalLogout();
// Verify that the session is regenerated if a module calls exit
// in hook_user_login().
user_save($user, array('name' => 'session_test_user'));
$user->name = 'session_test_user';
$this->drupalGet('session-test/id');
$matches = array();
preg_match('/\s*session_id:(.*)\n/', $this->drupalGetContent(), $matches);
$this->assertTrue(!empty($matches[1]) , t('Found session ID before logging in.'));
$original_session = $matches[1];
// We cannot use $this->drupalLogin($user); because we exit in
// session_test_user_login() which breaks a normal assertion.
$edit = array(
'name' => $user->name,
'pass' => $user->pass_raw
);
$this->drupalPost('user', $edit, t('Log in'));
$this->drupalGet('node');
$pass = $this->assertText($user->name, t('Found name: %name', array('%name' => $user->name)), t('User login'));
$this->_logged_in = $pass;
$this->drupalGet('session-test/id');
$matches = array();
preg_match('/\s*session_id:(.*)\n/', $this->drupalGetContent(), $matches);
$this->assertTrue(!empty($matches[1]) , t('Found session ID after logging in.'));
$this->assertTrue($matches[1] != $original_session, t('Session ID changed after login.'));
}
/**
* Test data persistence via the session_test module callbacks. Also tests
* drupal_session_count() since session data is already generated here.
*/
function testDataPersistence() {
$user = $this->drupalCreateUser(array('access content'));
// Enable sessions.
$this->sessionReset($user->uid);
$this->drupalLogin($user);
$this->session_count_authenticated = $this->session_count++;
$value_1 = $this->randomName();
$this->drupalGet('session-test/set/' . $value_1);
$this->assertText($value_1, t('The session value was stored.'), t('Session'));
$this->drupalGet('session-test/get');
$this->assertText($value_1, t('Session correctly returned the stored data for an authenticated user.'), t('Session'));
// Attempt to write over val_1. If drupal_save_session(FALSE) is working.
// properly, val_1 will still be set.
$value_2 = $this->randomName();
$this->drupalGet('session-test/no-set/' . $value_2);
$this->assertText($value_2, t('The session value was correctly passed to session-test/no-set.'), t('Session'));
$this->drupalGet('session-test/get');
$this->assertText($value_1, t('Session data is not saved for drupal_save_session(FALSE).'), t('Session'));
// Switch browser cookie to anonymous user, then back to user 1.
$this->sessionReset();
$this->sessionReset($user->uid);
$this->assertText($value_1, t('Session data persists through browser close.'), t('Session'));
// Logout the user and make sure the stored value no longer persists.
$this->drupalLogout();
$this->sessionReset();
$this->drupalGet('session-test/get');
// Session count should go up since we're accessing anonymously now.
$this->session_count_anonymous = $this->session_count++;
$this->assertNoText($value_1, t("After logout, previous user's session data is not available."), t('Session'));
$value_3 = $this->randomName();
$this->drupalGet('session-test/set/' . $value_3);
$this->assertText($value_3, t('Session data stored for anonymous user.'), t('Session'));
$this->drupalGet('session-test/get');
$this->assertText($value_3, t('Session correctly returned the stored data for an anonymous user.'), t('Session'));
$value_4 = $this->randomName();
$this->drupalGet('session-test/no-set/' . $value_4);
$this->assertText($value_4, t('The session value was correctly passed to session-test/no-set.'), t('Session'));
$this->drupalGet('session-test/get');
$this->assertText($value_3, t('Session data is not saved for drupal_save_session(FALSE).'), t('Session'));
// Logout and get first user back in. Sessions shouldn't persist through
// logout, so the data won't be on the page.
$this->drupalLogin($user);
$this->sessionReset($user->uid);
$this->drupalGet('session-test/get');
$this->assertNoText($value_1, t('Session has persisted for an authenticated user after logging out and then back in.'), t('Session'));
// Logout and create another user.