Bug 950399 - SessionStore shouldn't forget domain cookies r=yoric
authorTim Taubert <ttaubert@mozilla.com>
Thu, 15 Jan 2015 11:22:21 +0100
changeset 224322 17b3a6d3ee1a126de2becd6bdc99798ef4898aa5
parent 224321 f5053a5e716adcc612bf415cc23121f96381dc4f
child 224323 def4cd55d1f99ff281d5e4fdc732b49df6cf6df5
push id54190
push userkwierso@gmail.com
push dateSat, 17 Jan 2015 02:06:29 +0000
treeherdermozilla-inbound@369a8f14ccf8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersyoric
bugs950399
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 950399 - SessionStore shouldn't forget domain cookies r=yoric
browser/components/sessionstore/SessionCookies.jsm
--- a/browser/components/sessionstore/SessionCookies.jsm
+++ b/browser/components/sessionstore/SessionCookies.jsm
@@ -256,16 +256,50 @@ let SessionCookiesInternal = {
     let iter = Services.cookies.enumerator;
     while (iter.hasMoreElements()) {
       this._updateCookie(iter.getNext());
     }
   }
 };
 
 /**
+ * Generates all possible subdomains for a given host and prepends a leading
+ * dot to all variants.
+ *
+ * See http://tools.ietf.org/html/rfc6265#section-5.1.3
+ *     http://en.wikipedia.org/wiki/HTTP_cookie#Domain_and_Path
+ *
+ * All cookies belonging to a web page will be internally represented by a
+ * nsICookie object. nsICookie.host will be the request host if no domain
+ * parameter was given when setting the cookie. If a specific domain was given
+ * then nsICookie.host will contain that specific domain and prepend a leading
+ * dot to it.
+ *
+ * We thus generate all possible subdomains for a given domain and prepend a
+ * leading dot to them as that is the value that was used as the map key when
+ * the cookie was set.
+ */
+function* getPossibleSubdomainVariants(host) {
+  // Try given domain with a leading dot (.www.example.com).
+  yield "." + host;
+
+  // Stop if there are only two parts left (e.g. example.com was given).
+  let parts = host.split(".");
+  if (parts.length < 3) {
+    return;
+  }
+
+  // Remove the first subdomain (www.example.com -> example.com).
+  let rest = parts.slice(1).join(".");
+
+  // Try possible parent subdomains.
+  yield* getPossibleSubdomainVariants(rest);
+}
+
+/**
  * The internal cookie storage that keeps track of every active session cookie.
  * These are stored using maps per host, path, and cookie name.
  */
 let CookieStore = {
   /**
    * The internal structure holding all known cookies.
    *
    * Host =>
@@ -280,36 +314,54 @@ let CookieStore = {
    *       "username": {name: "username", value: "my_name_is", etc...},
    *       "sessionid": {name: "sessionid", value: "1fdb3a", etc...}
    *     }
    *   },
    *   "tbpl.mozilla.org": {
    *     "/path": {
    *       "cookiename": {name: "cookiename", value: "value", etc...}
    *     }
+   *   },
+   *   ".example.com": {
+   *     "/path": {
+   *       "cookiename": {name: "cookiename", value: "value", etc...}
+   *     }
    *   }
    * };
    */
   _hosts: new Map(),
 
   /**
    * Returns the list of stored session cookies for a given host.
    *
    * @param host
    *        A string containing the host name we want to get cookies for.
    */
   getCookiesForHost: function (host) {
-    if (!this._hosts.has(host)) {
-      return [];
+    let cookies = [];
+
+    let appendCookiesForHost = host => {
+      if (!this._hosts.has(host)) {
+        return;
+      }
+
+      for (let pathToNamesMap of this._hosts.get(host).values()) {
+        cookies.push(...pathToNamesMap.values());
+      }
     }
 
-    let cookies = [];
-
-    for (let pathToNamesMap of this._hosts.get(host).values()) {
-      cookies.push(...pathToNamesMap.values());
+    // Try to find cookies for the given host, e.g. <www.example.com>.
+    // The full hostname will be in the map if the Set-Cookie header did not
+    // have a domain= attribute, i.e. the cookie will only be stored for the
+    // request domain. Also, try to find cookies for subdomains, e.g.
+    // <.example.com>. We will find those variants with a leading dot in the
+    // map if the Set-Cookie header had a domain= attribute, i.e. the cookie
+    // will be stored for a parent domain and we send it for any subdomain.
+    for (let variant of [host, ...getPossibleSubdomainVariants(host)]) {
+      appendCookiesForHost(variant);
     }
 
     return cookies;
   },
 
   /**
    * Stores a given cookie.
    *