=== modified file 'includes/session.inc'
--- includes/session.inc	2010-06-05 13:30:42 +0000
+++ includes/session.inc	2010-06-09 00:59:20 +0000
@@ -147,6 +147,7 @@ function _drupal_session_write($sid, $va
       return;
     }
 
+    // Either ssid or sid or both will be added from $key below.
     $fields = array(
       'uid' => $user->uid,
       'cache' => isset($user->cache) ? $user->cache : 0,
@@ -154,17 +155,22 @@ function _drupal_session_write($sid, $va
       'session' => $value,
       'timestamp' => REQUEST_TIME,
     );
-    $key = array('sid' => $sid);
+
+    // The "secure pages" setting allows a site to simultaneously use both secure
+    // and insecure session cookies. If enabled and both cookies are presented
+    // then use both keys. If not enabled but on HTTPS then use the PHP session
+    // id as 'ssid'. If not on HTTP then use the PHP session id as 'sid'.
     if ($is_https) {
       $key['ssid'] = $sid;
       $insecure_session_name = substr(session_name(), 1);
-      // The "secure pages" setting allows a site to simultaneously use both
-      // secure and insecure session cookies. If enabled, use the insecure session
-      // identifier as the sid.
       if (variable_get('https', FALSE) && isset($_COOKIE[$insecure_session_name])) {
         $key['sid'] = $_COOKIE[$insecure_session_name];
       }
     }
+    else {
+      $key['sid'] = $sid;
+    }
+
     db_merge('sessions')
       ->key($key)
       ->fields($fields)
@@ -198,11 +204,11 @@ function _drupal_session_write($sid, $va
  * Initialize the session handler, starting a session if needed.
  */
 function drupal_session_initialize() {
-  global $user;
+  global $user, $is_https;
 
   session_set_save_handler('_drupal_session_open', '_drupal_session_close', '_drupal_session_read', '_drupal_session_write', '_drupal_session_destroy', '_drupal_session_garbage_collection');
 
-  if (isset($_COOKIE[session_name()])) {
+  if (isset($_COOKIE[session_name()]) || ($is_https && variable_get('https', FALSE) && isset($_COOKIE[substr(session_name(), 1)]))) {
     // If a session cookie exists, initialize the session. Otherwise the
     // session is only started on demand in drupal_session_commit(), making
     // anonymous users not use a session cookie unless something is stored in
@@ -298,6 +304,9 @@ function drupal_session_regenerate() {
   global $user, $is_https;
   if ($is_https && variable_get('https', FALSE)) {
     $insecure_session_name = substr(session_name(), 1);
+    if (isset($_COOKIE[$insecure_session_name])) {
+      $old_insecure_session_id = $_COOKIE[$insecure_session_name];
+    }
     $params = session_get_cookie_params();
     $session_id = drupal_hash_base64(uniqid(mt_rand(), TRUE) . drupal_random_bytes(55));
     setcookie($insecure_session_name, $session_id, REQUEST_TIME + $params['lifetime'], $params['path'], $params['domain'], FALSE, $params['httponly']);
@@ -318,11 +327,27 @@ function drupal_session_regenerate() {
   }
 
   if (isset($old_session_id)) {
+    $fields = array('sid' => session_id());
+    if ($is_https) {
+      $fields['ssid'] = session_id();
+      // If the "secure pages" setting is enabled, use the newly-created
+      // insecure session identifier as the regenerated sid.
+      if (variable_get('https', FALSE)) {
+        $fields['sid'] = $session_id;
+      }
+    }
+    db_update('sessions')
+      ->fields($fields)
+      ->condition($is_https ? 'ssid' : 'sid', $old_session_id)
+      ->execute();
+  }
+  elseif (isset($old_insecure_session_id)) {
+    // If logging in to the secure site, and there was no active session on the
+    // secure site but a session was active on the insecure site, update the
+    // insecure session with the new session identifiers.
     db_update('sessions')
-      ->fields(array(
-        $is_https ? 'ssid' : 'sid' => session_id()
-      ))
-      ->condition('sid', $old_session_id)
+      ->fields(array('sid' => $session_id, 'ssid' => session_id()))
+      ->condition('sid', $old_insecure_session_id)
       ->execute();
   }
   date_default_timezone_set(drupal_get_user_timezone());

=== modified file 'modules/simpletest/tests/session.test'
--- modules/simpletest/tests/session.test	2010-05-12 08:26:14 +0000
+++ modules/simpletest/tests/session.test	2010-06-09 00:59:56 +0000
@@ -274,7 +274,7 @@ class SessionHttpsTestCase extends Drupa
     // Check insecure cookie is not set.
     $this->assertFalse(isset($this->cookies[$insecure_session_name]));
     $ssid = $this->cookies[$secure_session_name]['value'];
-    $this->assertSessionIds($ssid, $ssid, 'Session has two secure SIDs');
+    $this->assertSessionIds('', $ssid, 'Session has NULL for SID and a correct secure SID.');
     $cookie = $secure_session_name . '=' . $ssid;
 
     // Verify that user is logged in on secure URL.
@@ -303,7 +303,9 @@ class SessionHttpsTestCase extends Drupa
     variable_set('https', TRUE);
 
     $this->curlClose();
-    $this->drupalGet('session-test/set/1');
+    // Start an anonymous session on the insecure site.
+    $session_data = $this->randomName();
+    $this->drupalGet('session-test/set/' . $session_data);
     // Check secure cookie on insecure page.
     $this->assertFalse(isset($this->cookies[$secure_session_name]), 'The secure cookie is not sent on insecure pages.');
     // Check insecure cookie on insecure page.
@@ -339,6 +341,11 @@ class SessionHttpsTestCase extends Drupa
       $secure_session_name . '=' . $ssid,
     );
 
+    // Test that session data saved before login is still available on the
+    // authenticated session.
+    $this->drupalGet('session-test/get');
+    $this->assertText($session_data, 'Session correctly returned the stored data set by the anonymous session.');
+
     foreach ($cookies as $cookie_key => $cookie) {
       foreach (array('admin/config', $this->httpsUrl('admin/config')) as $url_key => $url) {
         $this->curlClose();
@@ -354,6 +361,27 @@ class SessionHttpsTestCase extends Drupa
         }
       }
     }
+
+    // Clear browser cookie jar.
+    $this->cookies = array();
+
+    // Start an anonymous session on the secure site.
+    $this->drupalGet($this->httpsUrl('session-test/set/1'));
+
+    // Mock a login to the secure site using the secure session cookie.
+    $this->drupalGet('user');
+    $form = $this->xpath('//form[@id="user-login"]');
+    $form[0]['action'] = $this->httpsUrl('user');
+    $this->drupalPost(NULL, $edit, t('Log in'), array(), array('Cookie: ' . $secure_session_name . '=' . $this->cookies[$secure_session_name]['value']));
+
+    // Get the insecure session cookie set by the secure login POST request.
+    $headers = $this->drupalGetHeaders(TRUE);
+    strtok($headers[0]['set-cookie'], ';=');
+    $session_id = strtok(';=');
+
+    // Test that the user is also authenticated on the insecure site.
+    $this->drupalGet("user/{$user->uid}/edit", array(), array('Cookie: ' . $insecure_session_name . '=' . $session_id));
+    $this->assertResponse(200);
   }
 
   /**
@@ -375,7 +403,7 @@ class SessionHttpsTestCase extends Drupa
       ':sid' => $sid,
       ':ssid' => $ssid,
     );
-    return $this->assertTrue(db_query('SELECT sid FROM {sessions} WHERE sid = :sid AND ssid = :ssid', $args)->fetchField(), $assertion_text);
+    return $this->assertTrue(db_query('SELECT timestamp FROM {sessions} WHERE sid = :sid AND ssid = :ssid', $args)->fetchField(), $assertion_text);
   }
 
   protected function httpsUrl($url) {

=== modified file 'modules/system/system.install'
--- modules/system/system.install	2010-06-08 05:44:07 +0000
+++ modules/system/system.install	2010-06-09 00:59:20 +0000
@@ -1413,8 +1413,8 @@ function system_schema() {
         'description' => "Unique key: Secure session ID. The value is generated by PHP's Session API.",
         'type' => 'varchar',
         'length' => 64,
-        'not null' => FALSE,
-        'default' => NULL,
+        'not null' => TRUE,
+        'default' => '',
       ),
       'hostname' => array(
         'description' => 'The IP address that last used this session ID (sid).',
@@ -1442,14 +1442,14 @@ function system_schema() {
         'size' => 'big',
       ),
     ),
-    'primary key' => array('sid'),
+    'primary key' => array(
+      'sid',
+      'ssid',
+    ),
     'indexes' => array(
       'timestamp' => array('timestamp'),
       'uid' => array('uid'),
     ),
-    'unique keys' => array(
-      'ssid' => array('ssid'),
-    ),
     'foreign keys' => array(
       'uid' => array('users' => 'uid'),
     ),

=== modified file 'modules/system/system.queue.inc'
--- modules/system/system.queue.inc	2010-04-26 13:02:40 +0000
+++ modules/system/system.queue.inc	2010-06-10 07:33:14 +0000
@@ -24,7 +24,8 @@
  * DrupalQueueInterface::deleteItem(). If the consumer dies, the item will be
  * made available again by the DrupalQueueInterface implementation once the
  * lease expires. Another consumer will then be able to receive it when calling
- * DrupalQueueInterface::claimItem().
+ * DrupalQueueInterface::claimItem(). Due to this, the processing code should
+ * be aware that an item might be handed over for processing more than once.
  *
  * The $item object used by the DrupalQueueInterface can contain arbitrary
  * metadata depending on the implementation. Systems using the interface should
@@ -33,18 +34,23 @@
  * DrupalQueueInterface::claimItem() needs to be passed to
  * DrupalQueueInterface::deleteItem() once processing is completed.
  *
- * While the queue system makes a best effort to preserve order in messages,
- * due to the pluggable nature of the queue, there is no guarantee that items
- * will be delivered on claim in the order they were sent. For example, some
- * implementations like beanstalkd or others with distributed back-ends like
+ * There are two kinds of queue backends available: reliable, which preserves
+ * the order of messages and guarantees that every item will be executed at
+ * least once. The non-reliable kind only does a best effort to preserve order
+ * in messages and to execute them at least once but there is a small chance
+ * that some items get lost. For example, some distributed back-ends like
  * Amazon SQS will be managing jobs for a large set of producers and consumers
- * where a strict FIFO ordering will likely not be preserved.
- *
- * The system also makes no guarantees about a task only being executed once:
- * callers that have non-idempotent tasks either need to live with the
- * possiblity of the task being invoked multiple times in cases where a claim
- * lease expires, or need to implement their own transactions to make their
- * tasks idempotent.
+ * where a strict FIFO ordering will likely not be preserved. Another example
+ * would be an in-memory queue backend which might lose items if it crashes.
+ * However, such a backend would be able to deal with significantly more writes
+ * than a reliable queue and for many tasks this is more important. See
+ * aggregator_cron() for an example of how can this not be a problem. Another
+ * example is doing Twitter statistics -- the small possibility of losing a few
+ * items is insignificant next to power of the queue being able to keep up with
+ * writes. As described in the processing section, regardless of the queue
+ * being reliable or not, the processing code should be aware that an item
+ * might be handed over for processing more than once (because the processing
+ * code might time out before it finishes).
  */
 
 /**
@@ -52,21 +58,38 @@
  */
 class DrupalQueue {
   /**
-   * Get a queue object for a given name.
+   * Return the queue object for a given name.
+   *
+   * The following variables can be set by variable_set or $conf overrides:
+   * - queue_class_$name: the class to be used for the queue $name.
+   * - queue_default_class: the class to use when queue_class_$name is not
+   *   defined. Defaults to SystemQueue, a reliable backend using SQL.
+   * - queue_default_reliable_class: the class to use when queue_class_$name is
+   *   not defined and the queue_default_class is not reliable. Defaults to
+   *   SystemQueue.
    *
    * @param $name
    *   Arbitrary string. The name of the queue to work with.
+   * @param $reliable
+   *   TRUE if the ordering of items and guaranteeing every item executes at
+   *   least once is important, FALSE if scalability is the main concern.
+   *
    * @return
    *   The queue object for a given name.
    */
-  public static function get($name) {
+  public static function get($name, $reliable = FALSE) {
     static $queues;
     if (!isset($queues[$name])) {
       $class = variable_get('queue_class_' . $name, NULL);
       if (!$class) {
         $class = variable_get('queue_default_class', 'SystemQueue');
       }
-      $queues[$name] = new $class($name);
+      $object = new $class($name);
+      if ($reliable && !$object instanceof DrupalReliableQueueInterface) {
+        $class = variable_get('queue_default_reliable_class', 'SystemQueue');
+        $object = new $class($name);
+      }
+      $queues[$name] = $object;
     }
     return $queues[$name];
   }
@@ -89,8 +112,8 @@ interface DrupalQueueInterface {
    * @return
    *   TRUE if the item was successfully created and was (best effort) added
    *   to the queue, otherwise FALSE. We don't guarantee the item was
-   *   committed to disk, that your disk wasn't hit by a meteor, etc, but as
-   *   far as we know, the item is now in the queue.
+   *   committed to disk etc, but as far as we know, the item is now in the
+   *   queue.
    */
   public function createItem($data);
 
@@ -167,9 +190,18 @@ interface DrupalQueueInterface {
 }
 
 /**
+ * Reliable queue interface.
+ *
+ * Classes implementing this interface preserve the order of messages and
+ * guarantee that every item will be executed at least once.
+ */
+interface DrupalReliableQueueInterface extends DrupalQueueInterface {
+}
+
+/**
  * Default queue implementation.
  */
-class SystemQueue implements DrupalQueueInterface {
+class SystemQueue implements DrupalReliableQueueInterface {
   /**
    * The name of the queue this instance is working with.
    *

