#370454 by Damien Tournoud: simplify and clean-up page caching.

From: damz <damz@damz-dev.local>


---
 batch.inc                            |    2 
 bootstrap.inc                        |  104 +++++++++++++------------
 common.inc                           |   42 ++++------
 form.inc                             |    2 
 session.inc                          |  142 +++++++++++++++++++++-------------
 install.php                          |    2 
 book/book.install                    |    4 -
 dblog/dblog.admin.inc                |    6 +
 node/node.admin.inc                  |    6 +
 openid/openid.module                 |    4 -
 simpletest/tests/session.test        |  113 +++++++++------------------
 simpletest/tests/session_test.module |   22 ++---
 system/system.admin.inc              |    4 -
 system/system.install                |    8 +-
 user/user.admin.inc                  |    6 +
 user/user.module                     |    2 
 update.php                           |    8 +-
 17 files changed, 230 insertions(+), 247 deletions(-)

diff --git includes/batch.inc includes/batch.inc
index dfd7d82..7e6be20 100644
--- includes/batch.inc
+++ includes/batch.inc
@@ -443,7 +443,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.
-    drupal_set_session('batch_form_state', $_batch['form_state']);
+    $_SESSION['batch_form_state'] = $_batch['form_state'];
     drupal_goto($_batch['source_page']);
   }
 }
diff --git includes/bootstrap.inc includes/bootstrap.inc
index 6f47c6e..204cc32 100644
--- includes/bootstrap.inc
+++ includes/bootstrap.inc
@@ -671,7 +671,6 @@ function variable_del($name) {
   unset($conf[$name]);
 }
 
-
 /**
  * Retrieve the current page from the cache.
  *
@@ -680,36 +679,34 @@ function variable_del($name) {
  * 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.
+ *   The cache object, if the page was found in the cache, NULL otherwise.
  */
-function page_get_cache($retrieve) {
-  global $user, $base_root;
-  static $ob_started = FALSE;
+function drupal_page_get_cache() {
+  global $base_root;
 
-  if ($user->uid || ($_SERVER['REQUEST_METHOD'] != 'GET' && $_SERVER['REQUEST_METHOD'] != 'HEAD') || count(drupal_get_messages(NULL, FALSE)) || $_SERVER['SERVER_SOFTWARE'] === 'PHP CLI') {
-    return FALSE;
+  if (drupal_page_is_cacheable()) {
+    return cache_get($base_root . request_uri(), 'cache_page');
   }
-  if ($retrieve) {
-    $cache = cache_get($base_root . request_uri(), 'cache_page');
-    if ($cache) {
-      return $cache;
-    }
-    else {
-      ob_start();
-      $ob_started = TRUE;
-    }
+}
+
+/**
+ * Determine the cacheability of the current page.
+ *
+ * @param $force
+ *  Set to FALSE if you want to prevent this page to get cached.
+ * @return
+ *  TRUE if the current page can be cached, FALSE otherwise.
+ */
+function drupal_page_is_cacheable($force = NULL) {
+  static $forced_cache = TRUE;
+  if (isset($force)) {
+    $forced_cache = $force;
   }
-  return $ob_started;
+
+  return $forced_cache && !drupal_session_exists()
+    && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD')
+    && $_SERVER['SERVER_SOFTWARE'] !== 'PHP CLI';
 }
 
 /**
@@ -885,7 +882,7 @@ function drupal_send_headers($default_headers = array(), $only_default = FALSE)
  * the client think that the anonymous and authenticated pageviews are
  * identical.
  *
- * @see page_set_cache()
+ * @see drupal_page_set_cache()
  */
 function drupal_page_header() {
   $headers_sent = &drupal_static(__FUNCTION__, FALSE);
@@ -914,7 +911,7 @@ function drupal_page_header() {
  * and the conditions match those currently in the cache, a 304 Not Modified
  * response is sent.
  */
-function drupal_page_cache_header(stdClass $cache) {
+function drupal_serve_page_from_cache(stdClass $cache) {
   // Negotiate whether to use compression.
   $page_compression = variable_get('page_compression', TRUE) && extension_loaded('zlib');
   $return_compressed = $page_compression && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE;
@@ -1170,7 +1167,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'])) {
-      drupal_set_session('messages', array());
+      $_SESSION['messages'] = array();
     }
 
     if (!isset($_SESSION['messages'][$type])) {
@@ -1180,6 +1177,9 @@ function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) {
     if ($repeat || !in_array($message, $_SESSION['messages'][$type])) {
       $_SESSION['messages'][$type][] = $message;
     }
+
+    // Mark this page has being not cacheable.
+    drupal_page_is_cacheable(FALSE);
   }
 
   // Messages not set when DB connection fails.
@@ -1365,15 +1365,21 @@ 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');
+
       // If a session cookie exists, initialize the session. Otherwise the
-      // session is only started on demand in drupal_session_start(), making
+      // session is only started on demand in drupal_session_conditional_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();
+        session_start();
       }
       else {
         $user = drupal_anonymous_user();
+        // Set a session identifier for this request. This is necessary because
+        // we lazyly start sessions at the end of this request, and some
+        // processes (like drupal_get_token()) needs to know the future
+        // session ID in advance.
+        drupal_session_id();
       }
       break;
 
@@ -1384,22 +1390,26 @@ 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 ? FALSE : page_get_cache(TRUE);
+      if ($cache_mode != CACHE_DISABLED) {
+        $cache = drupal_page_get_cache();
+      }
+      else {
+        $cache = FALSE;
+      }
+
       // If the skipping of the bootstrap hooks is not enforced, call hook_boot.
-      if (!is_object($cache) || $cache_mode != CACHE_AGGRESSIVE) {
+      if (!$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 (is_object($cache)) {
-        // Destroy empty anonymous sessions.
-        if (drupal_session_is_started() && empty($_SESSION)) {
-          session_destroy();
-        }
+      if ($cache) {
         header('X-Drupal-Cache: HIT');
-        drupal_page_cache_header($cache);
+        drupal_serve_page_from_cache($cache);
         // If the skipping of the bootstrap hooks is not enforced, call hook_exit.
         if ($cache_mode != CACHE_AGGRESSIVE) {
           module_invoke_all('exit');
@@ -1408,19 +1418,13 @@ function _drupal_bootstrap($phase) {
         exit;
       }
 
-      // Prepare for non-cached page workflow.
-
-      // If the session has not already been started and output buffering is
-      // not enabled, the HTTP headers must be sent now, including the session
-      // cookie. If output buffering is enabled, the session may be started
-      // at any time using drupal_session_start().
-      if ($cache === FALSE) {
-        drupal_page_header();
-        drupal_session_start();
-      }
-      else {
+      if (!$cache && drupal_page_is_cacheable()) {
         header('X-Drupal-Cache: MISS');
       }
+
+      // Prepare for non-cached page workflow.
+      ob_start();
+      drupal_page_header();
       break;
 
     case DRUPAL_BOOTSTRAP_LANGUAGE:
diff --git includes/common.inc includes/common.inc
index f787251..c100fc7 100644
--- includes/common.inc
+++ includes/common.inc
@@ -325,11 +325,9 @@ function drupal_goto($path = '', $query = NULL, $fragment = NULL, $http_response
     module_invoke_all('exit', $url);
   }
 
-  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();
-  }
+  // Commit the session, if necessary. We need all session data written to the
+  // database before redirecting.
+  drupal_session_commit();
 
   header('Location: ' . $url, TRUE, $http_response_code);
 
@@ -2149,19 +2147,17 @@ function l($text, $path, array $options = array()) {
 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);
-  }
+  module_invoke_all('exit');
 
-  if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED) {
-    page_set_cache();
-  }
+  // Commit the user session, if it exists.
+  drupal_session_commit();
 
-  module_invoke_all('exit');
+  if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED && ($cache = drupal_page_set_cache())) {
+    drupal_serve_page_from_cache($cache);
+  }
+  else {
+    ob_flush();
+  }
 
   module_implements(MODULE_IMPLEMENTS_WRITE_CACHE);
   _registry_check_code(REGISTRY_WRITE_LOOKUP_CACHE);
@@ -3273,11 +3269,12 @@ function _drupal_bootstrap_full() {
  *
  * @see drupal_page_header
  */
-function page_set_cache() {
+function drupal_page_set_cache() {
   global $user, $base_root;
 
-  if (page_get_cache(FALSE)) {
+  if (drupal_page_is_cacheable()) {
     $cache_page = TRUE;
+
     $cache = (object) array(
       'cid' => $base_root . request_uri(),
       'data' => ob_get_clean(),
@@ -3285,12 +3282,14 @@ function page_set_cache() {
       '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.
@@ -3306,12 +3305,7 @@ function page_set_cache() {
     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();
+    return $cache;
   }
 }
 
diff --git includes/form.inc includes/form.inc
index b57888e..6bd1c38 100644
--- includes/form.inc
+++ includes/form.inc
@@ -2794,7 +2794,7 @@ function form_clean_id($id = NULL, $flush = FALSE) {
  *   foreach ($results as $result) {
  *     $items[] = t('Loaded node %title.', array('%title' => $result));
  *   }
- *   drupal_set_session('my_batch_results', $items);
+ *   $_SESSION['my_batch_results'] = $items;
  * }
  * @endcode
  */
diff --git includes/session.inc includes/session.inc
index 3958522..04b356f 100644
--- includes/session.inc
+++ includes/session.inc
@@ -101,7 +101,6 @@ function _sess_read($key) {
     $session = isset($user->session) ? $user->session : '';
     $user = drupal_anonymous_user($session);
   }
-
   return $user->session;
 }
 
@@ -128,7 +127,7 @@ function _sess_write($key, $value) {
   // 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))) {
+  if (!drupal_save_session() || empty($user) || (!$user->uid && empty($_COOKIE[session_name()]) && empty($value))) {
     return TRUE;
   }
 
@@ -158,86 +157,117 @@ function _sess_write($key, $value) {
 }
 
 /**
- * Propagate $_SESSION and set session cookie if not already set. This function
- * should be called before writing to $_SESSION, usually via
- * drupal_set_session().
+ * Commit the current session, if necessary.
  *
- * @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.
+ * If an anonymous user already have an empty session, destroy it.
  */
-function drupal_session_start($start = TRUE) {
-  $started = &drupal_static(__FUNCTION__, FALSE);
-  if ($start && !$started) {
-    $started = TRUE;
+function drupal_session_commit() {
+  global $user;
+
+  $write = FALSE;
+
+  if (drupal_session_id(TRUE)) {
+    if (!$user->uid && empty($_SESSION)) {
+      // Destroy empty anonymous sessions.
+      session_destroy();
+    }
+    else {
+      $write = TRUE;
+    }
+  }
+  else if (!empty($user->uid) || !empty($_SESSION)) {
+    // Save current session data before starting it, as PHP will destroy it.
+    $session_data = !empty($_SESSION) ? $_SESSION : array();
+    $current_user = $user;
+
     session_start();
+
+    // Restore session data.
+    $_SESSION += $session_data;
+    $user = $current_user;
+
+    // Now that we have started the session, reset the fake session id cache.
+    drupal_session_id(FALSE, TRUE);
+
+    $write = TRUE;
+  }
+
+  if ($write) {
+    // Write the session data.
+    session_write_close();
   }
-  return $started;
 }
 
 /**
- * Return whether a session has been started and the $_SESSION variable is
- * available.
+ * Return whether a session has or will be started.
  */
-function drupal_session_is_started() {
-  return drupal_session_start(FALSE);
+function drupal_session_exists() {
+  global $user;
+  return isset($_COOKIE[session_name()]) || drupal_session_id(TRUE) || !empty($_SESSION);
 }
 
 /**
- * Get a session variable.
+ * Return the session identifier for this request.
  *
- * @param $name
- *   The name of the variable to get. If not supplied, all variables are returned.
+ * By default, we do not start sessions for anonymous users. Those sessions
+ * are lazyly started at the end of the request, if necessary. The value returned
+ * by this function is the session ID that would be set in that case.
+ *
+ * @param $exclude_fake_session_id
+ *   Only returns the session ID if the session was actually started.
+ * @param $reset
+ *   Reset the session id after it has started.
  * @return
- *   The value of the variable, or FALSE if the variable is not set.
+ *   The session ID for this request.
  */
-function drupal_get_session($name = NULL) {
-  if (is_null($name)) {
-    return $_SESSION;
+function drupal_session_id($exclude_fake_session_id = FALSE, $reset = FALSE) {
+  static $fake_session_id = NULL;
+  if ($reset) {
+    $fake_session_id = NULL;
   }
-  elseif (isset($_SESSION[$name])) {
-    return $_SESSION[$name];
+
+  $session_id = session_id();
+
+  // Generate a new session id if none has been set yet.
+  if (!$session_id) {
+    $fake_session_id = md5(uniqid());
+    $session_id = $fake_session_id;
+    session_id($fake_session_id);
+  }
+
+  if ($exclude_fake_session_id && ($session_id === $fake_session_id)) {
+    return NULL;
   }
   else {
-    return FALSE;
+    return $session_id;
   }
 }
 
 /**
- * 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.
  */
 function drupal_session_regenerate() {
-  $old_session_id = session_id();
+  // Set the session cookie "httponly" flag to reduce the risk of session
+  // stealing via XSS.
   extract(session_get_cookie_params());
-  // Set "httponly" to TRUE to reduce the risk of session stealing via XSS.
   session_set_cookie_params($lifetime, $path, $domain, $secure, TRUE);
-  session_regenerate_id();
-  db_update('sessions')
-    ->fields(array(
-      'sid' => session_id()
-    ))
-    ->condition('sid', $old_session_id)
-    ->execute();
+
+  if ($old_session_id = drupal_session_id(TRUE)) {
+    session_regenerate_id();
+  }
+  else {
+    // Start the session when it doesn't exist yet.
+    drupal_session_commit();
+  }
+
+  if (isset($old_session_id)) {
+    db_update('sessions')
+      ->fields(array(
+        'sid' => session_id()
+      ))
+      ->condition('sid', $old_session_id)
+      ->execute();
+  }
 }
 
 /**
diff --git install.php install.php
index 60902f6..5ee2d6d 100644
--- install.php
+++ install.php
@@ -625,7 +625,7 @@ function install_tasks($profile, $task) {
   drupal_install_init_database();
 
   drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
-  drupal_set_session('messages', $messages);
+  $_SESSION['messages'] = $messages;
 
   // URL used to direct page requests.
   $url = $base_url . '/install.php?locale=' . $install_locale . '&profile=' . $profile;
diff --git modules/book/book.install modules/book/book.install
index 4523dd9..913609e 100644
--- modules/book/book.install
+++ modules/book/book.install
@@ -134,8 +134,8 @@ function book_update_6000() {
 
       db_create_table($ret, 'book', $schema['book']);
 
-      drupal_set_session('book_update_6000_orphans', array('from' => 0));
-      drupal_set_session('book_update_6000', array());
+      $_SESSION['book_update_6000_orphans'] = array('from' => 0);
+      $_SESSION['book_update_6000'] = array();
       $result = db_query("SELECT * from {book_temp} WHERE parent = 0");
 
       // Collect all books - top-level nodes.
diff --git modules/dblog/dblog.admin.inc modules/dblog/dblog.admin.inc
index 3867f3e..3034747 100644
--- modules/dblog/dblog.admin.inc
+++ modules/dblog/dblog.admin.inc
@@ -285,7 +285,7 @@ function _dblog_format_message($dblog) {
  */
 function dblog_filter_form() {
   if (!isset($_SESSION['dblog_overview_filter'])) {
-    drupal_set_session('dblog_overview_filter', array());
+    $_SESSION['dblog_overview_filter'] = array();
   }
   $session = &$_SESSION['dblog_overview_filter'];
   $filters = dblog_filters();
@@ -344,14 +344,14 @@ function dblog_filter_form_submit($form, &$form_state) {
       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'] = array();
           }
           $_SESSION['dblog_overview_filter'][$name] = $form_state['values'][$name];
         }
       }
       break;
     case t('Reset'):
-      drupal_set_session('dblog_overview_filter', array());
+      $_SESSION['dblog_overview_filter'] = array();
       break;
   }
   return 'admin/reports/dblog';
diff --git modules/node/node.admin.inc modules/node/node.admin.inc
index 55531be..f45a468 100644
--- modules/node/node.admin.inc
+++ modules/node/node.admin.inc
@@ -203,7 +203,7 @@ function node_build_filter_query() {
  */
 function node_filter_form() {
   if (!isset($_SESSION['node_overview_filter'])) {
-    drupal_set_session('node_overview_filter', array());
+    $_SESSION['node_overview_filter'] = array();
   }
   $session = &$_SESSION['node_overview_filter'];
   $filters = node_filters();
@@ -321,7 +321,7 @@ function node_filter_form_submit($form, &$form_state) {
 
         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();
           }
           $_SESSION['node_overview_filter'][] = array($filter, $form_state['values'][$filter]);
         }
@@ -331,7 +331,7 @@ function node_filter_form_submit($form, &$form_state) {
       array_pop($_SESSION['node_overview_filter']);
       break;
     case t('Reset'):
-      drupal_set_session('node_overview_filter', array());
+      $_SESSION['node_overview_filter'] = array();
       break;
   }
 }
diff --git modules/openid/openid.module modules/openid/openid.module
index 30f16bc..4ab4fdb 100644
--- modules/openid/openid.module
+++ modules/openid/openid.module
@@ -175,7 +175,7 @@ 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'] = array();
   }
   $_SESSION['openid']['service'] = $services[0];
   // Store the claimed id
@@ -435,7 +435,7 @@ function openid_authentication($response) {
       // 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'] = array();
       }
       $_SESSION['openid']['values'] = $form_state['values'];
       // We'll want to redirect back to the same place.
diff --git modules/simpletest/tests/session.test modules/simpletest/tests/session.test
index 2b0076f..06bfce7 100644
--- modules/simpletest/tests/session.test
+++ modules/simpletest/tests/session.test
@@ -31,12 +31,15 @@ class SessionTestCase extends DrupalWebTestCase {
 
     // 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->drupalGetHeader('Set-Cookie', TRUE)), 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'));
@@ -46,6 +49,7 @@ class SessionTestCase extends DrupalWebTestCase {
     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(
@@ -69,12 +73,18 @@ class SessionTestCase extends DrupalWebTestCase {
    * drupal_session_count() since session data is already generated here.
    */
   function testDataPersistence() {
+    // At the very start, we have no session.
+    $session_count = array(
+      'anonymous' => 0,
+      'authenticated' => 0,
+    );
+
     $user = $this->drupalCreateUser(array('access content'));
     // Enable sessions.
     $this->sessionReset($user->uid);
 
     $this->drupalLogin($user);
-    $this->session_count_authenticated = $this->session_count++;
+    $session_count['authenticated']++;
 
     $value_1 = $this->randomName();
     $this->drupalGet('session-test/set/' . $value_1);
@@ -97,48 +107,53 @@ class SessionTestCase extends DrupalWebTestCase {
 
     // Logout the user and make sure the stored value no longer persists.
     $this->drupalLogout();
+    $session_count['authenticated']--;
+
     $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'));
 
+    // Now try to store some data as an anonymous user.
     $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'));
+    // Session count should go up since we have started an anonymous session now.
+    $session_count['anonymous']++;
 
+    // Try to store data when drupal_save_session(FALSE).
     $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.
+    // Login, the data should persist.
     $this->drupalLogin($user);
+    $session_count['anonymous']--;
+    $session_count['authenticated']++;
     $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.
+    // Change session and create another user.
     $user2 = $this->drupalCreateUser(array('access content'));
     $this->sessionReset($user2->uid);
     $this->drupalLogin($user2);
-    $this->session_count_authenticated = $this->session_count++;
+    $session_count['authenticated']++;
 
     // Perform drupal_session_count tests here in order to use the session data already generated.
     // Test absolute count.
     $anonymous = drupal_session_count(0, TRUE);
     $authenticated = drupal_session_count(0, FALSE);
-    $this->assertEqual($anonymous + $authenticated, $this->session_count, t('Correctly counted @count total sessions.', array('@count' => $this->session_count)), t('Session'));
+    $this->assertEqual($anonymous + $authenticated, $session_count['anonymous'] + $session_count['authenticated'], t('Correctly counted @count total sessions.', array('@count' => 1)), t('Session'));
 
     // Test anonymous count.
-    $this->assertEqual($anonymous, $this->session_count_anonymous, t('Correctly counted @count anonymous sessions.', array('@count' => $anonymous)), t('Session'));
+    $this->assertEqual($anonymous, $session_count['anonymous'], t('Correctly counted @count anonymous sessions.', array('@count' => $anonymous)), t('Session'));
 
     // Test authenticated count.
-    $this->assertEqual($authenticated, $this->session_count_authenticated, t('Correctly counted @count authenticated sessions.', array('@count' => $authenticated)), t('Session'));
+    $this->assertEqual($authenticated, $session_count['authenticated'], t('Correctly counted @count authenticated sessions.', array('@count' => $authenticated)), t('Session'));
 
     // Should return 0 sessions from 1 second from now.
     $this->assertEqual(drupal_session_count(time() + 1), 0, t('Correctly returned 0 sessions newer than the current time.'), t('Session'));
@@ -149,83 +164,39 @@ class SessionTestCase extends DrupalWebTestCase {
    * Test that empty anonymous sessions are destroyed.
    */
   function testEmptyAnonymousSession() {
-    // With caching disabled, a session is always started.
+    // Verify that no session is automatically created for anonymous user.
     $this->drupalGet('');
     $this->assertSessionCookie(FALSE);
-    $this->assertSessionStarted(TRUE);
     $this->assertSessionEmpty(TRUE);
 
+    // The same behavior is expected when caching is enabled.
     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->assertSessionCookie(FALSE);
     $this->assertSessionEmpty(TRUE);
     $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 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->assertSessionCookie(TRUE);
     $this->assertTrue($this->drupalGetHeader('Set-Cookie'), t('New session was started.'));
 
-    // Display the message.
+    // Display the message, during the same request the session is destroyed
+    // and the session cookie is unset.
     $this->drupalGet('');
-    $this->assertSessionCookie(TRUE);
-    $this->assertSessionStarted(TRUE);
+    $this->assertSessionCookie(FALSE);
     $this->assertSessionEmpty(FALSE);
     $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), t('Caching was bypassed.'));
     $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->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 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->assertSessionEmpty(TRUE);
+    $this->assertNoText(t('This is a dummy message.'), t('Message was not cached.'));
     $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 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();
   }
 
   /**
@@ -250,22 +221,10 @@ class SessionTestCase extends DrupalWebTestCase {
    */
   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.'));
+      $this->assertNotNull($this->session_id, t('Session cookie was sent.'));
     }
     else {
-      $this->assertIdentical($this->drupalGetHeader('X-Session-Started'), '0', t('Session was not started.'));
+      $this->assertNull($this->session_id, t('Session cookie was not sent.'));
     }
   }
 
diff --git modules/simpletest/tests/session_test.module modules/simpletest/tests/session_test.module
index c24b721..02b9930 100644
--- modules/simpletest/tests/session_test.module
+++ modules/simpletest/tests/session_test.module
@@ -51,22 +51,10 @@ function session_test_menu() {
  * Implement hook_boot().
  */
 function session_test_boot() {
-  header('X-Session-Cookie: ' . intval(isset($_COOKIE[session_name()])));
-  header('X-Session-Started: ' . intval(drupal_session_is_started()));
   header('X-Session-Empty: ' . intval(empty($_SESSION)));
 }
 
 /**
- * Implement hook_init().
- */
-function session_test_init() {
-  // hook_init() is called later in the bootstrap process, but not in cached
-  // requests. Here the header set in hook_boot() is overwritten, so the
-  // session state is reported as late in the bootstrap process as possible.
-  header('X-Session-Started: ' . intval(drupal_session_is_started()));
-}
-
-/**
  * Page callback, prints the stored session value to the screen.
  */
 function _session_test_get() {
@@ -82,7 +70,7 @@ function _session_test_get() {
  * Page callback, stores a value in $_SESSION['session_test_value'].
  */
 function _session_test_set($value) {
-  drupal_set_session('session_test_value', $value);
+  $_SESSION['session_test_value'] = $value;
   return t('The current value of the stored session variable has been set to %val', array('%val' => $value));
 }
 
@@ -100,6 +88,12 @@ function _session_test_no_set($value) {
  * Menu callback: print the current session ID.
  */
 function _session_test_id() {
+  // Set a value in $_SESSION, so that drupal_session_commit() will start
+  // a session.
+  $_SESSION['test'] = 'test';
+
+  drupal_session_commit();
+
   return 'session_id:' . session_id() . "\n";
 }
 
@@ -119,7 +113,7 @@ function _session_test_set_message() {
  * having started the session in advance.
  */
 function _session_test_set_not_started() {
-  if (!drupal_session_is_started()) {
+  if (!drupal_session_exists()) {
     $_SESSION['session_test_value'] = t('Session was not started');
   }
 }
diff --git modules/system/system.admin.inc modules/system/system.admin.inc
index abdcfb3..6a3bc4f 100644
--- modules/system/system.admin.inc
+++ modules/system/system.admin.inc
@@ -1734,7 +1734,7 @@ function system_clean_url_settings() {
   // When accessing this form using a non-clean URL, allow a re-check to make
   // sure clean URLs can be disabled at all times.
   $available = FALSE;
-  if (strpos(request_uri(), '?q=') === FALSE || drupal_get_session('clean_url')) {
+  if (strpos(request_uri(), '?q=') === FALSE || !empty($_SESSION['clean_url'])) {
     $available = TRUE;
   }
   else {
@@ -1745,7 +1745,7 @@ function system_clean_url_settings() {
   }
 
   if ($available) {
-    drupal_set_session('clean_url', TRUE);
+    $_SESSION['clean_url'] = TRUE;
 
     $form['clean_url'] = array(
       '#type' => 'checkbox',
diff --git modules/system/system.install modules/system/system.install
index f0505aa..2326d76 100644
--- modules/system/system.install
+++ modules/system/system.install
@@ -1972,10 +1972,10 @@ function system_update_6021() {
   // Multi-part update
   if (!isset($_SESSION['system_update_6021'])) {
     db_add_field($ret, 'menu', 'converted', array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'size' => 'tiny'));
-    drupal_set_session('system_update_6021_max', db_result(db_query('SELECT COUNT(*) FROM {menu}')));
-    drupal_set_session('menu_menu_map', array(1 => 'navigation'));
+    $_SESSION['system_update_6021_max'] = db_result(db_query('SELECT COUNT(*) FROM {menu}'));
+    $_SESSION['menu_menu_map'] = array(1 => 'navigation');
     // 0 => FALSE is for new menus, 1 => FALSE is for the navigation.
-    drupal_set_session('menu_item_map', array(0 => FALSE, 1 => FALSE));
+    $_SESSION['menu_item_map'] = array(0 => FALSE, 1 => FALSE);
     $table = array(
       'fields' => array(
         'menu_name'   => array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => ''),
@@ -1986,7 +1986,7 @@ function system_update_6021() {
     );
     db_create_table($ret, 'menu_custom', $table);
     db_query("INSERT INTO {menu_custom} (menu_name, title, description) VALUES ('%s', '%s', '%s')", $menus['navigation']);
-    drupal_set_session('system_update_6021', 0);
+    $_SESSION['system_update_6021'] = 0;
   }
 
   $limit = 50;
diff --git modules/user/user.admin.inc modules/user/user.admin.inc
index 38c3bfb..d77a066 100644
--- modules/user/user.admin.inc
+++ modules/user/user.admin.inc
@@ -34,7 +34,7 @@ function user_admin($callback_arg = '') {
  */
 function user_filter_form() {
   if (!isset($_SESSION['user_overview_filter'])) {
-    drupal_set_session('user_overview_filter', array());
+    $_SESSION['user_overview_filter'] = array();
   }
   $session = &$_SESSION['user_overview_filter'];
   $filters = user_filters();
@@ -104,7 +104,7 @@ function user_filter_form_submit($form, &$form_state) {
         $options = $filter == 'permission' ? call_user_func_array('array_merge', $filters[$filter]['options']) : $filters[$filter]['options'];
         if (isset($options[$form_state['values'][$filter]])) {
           if (!isset($_SESSION['user_overview_filter'])) {
-            drupal_set_session('user_overview_filter', array());
+            $_SESSION['user_overview_filter'] = array();
           }
           $_SESSION['user_overview_filter'][] = array($filter, $form_state['values'][$filter]);
         }
@@ -114,7 +114,7 @@ function user_filter_form_submit($form, &$form_state) {
       array_pop($_SESSION['user_overview_filter']);
       break;
     case t('Reset'):
-      drupal_set_session('user_overview_filter', array());
+      $_SESSION['user_overview_filter'] = array();
       break;
     case t('Update'):
       return;
diff --git modules/user/user.module modules/user/user.module
index ba3b19f..a5a4de2 100644
--- modules/user/user.module
+++ modules/user/user.module
@@ -1715,10 +1715,12 @@ function user_authenticate_finalize(&$edit) {
     ->fields(array('login' => $user->login))
     ->condition('uid', $user->uid)
     ->execute();
+
   // Regenerate the session ID to prevent against session fixation attacks.
   // This is called before hook_user in case one of those functions fails
   // or incorrectly does a redirect which would leave the old session in place.
   drupal_session_regenerate();
+
   user_module_invoke('login', $edit, $user);
 }
 
diff --git update.php update.php
index 99a8cc2..e579789 100644
--- update.php
+++ update.php
@@ -292,7 +292,7 @@ function update_batch() {
 
   // During the update, bring the site offline so that schema changes do not
   // affect visiting users.
-  drupal_set_session('site_offline', variable_get('site_offline', FALSE));
+  $_SESSION['site_offline'] = variable_get('site_offline', FALSE);
   if ($_SESSION['site_offline'] == FALSE) {
     variable_set('site_offline', TRUE);
   }
@@ -326,9 +326,9 @@ function update_finished($success, $results, $operations) {
   // clear the caches in case the data has been updated.
   drupal_flush_all_caches();
 
-  drupal_set_session('update_results', $results);
-  drupal_set_session('update_success', $success);
-  drupal_set_session('updates_remaining', $operations);
+  $_SESSION['update_results'] = $results;
+  $_SESSION['update_success'] = $success;
+  $_SESSION['updates_remaining'] = $operations;
 
   // Now that the update is done, we can put the site back online if it was
   // previously turned off.
