summaryrefslogtreecommitdiffstats
path: root/net/base/cookie_monster.cc
diff options
context:
space:
mode:
authorrdsmith@chromium.org <rdsmith@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-07 19:33:26 +0000
committerrdsmith@chromium.org <rdsmith@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-07 19:33:26 +0000
commit7a964a7f1edad8c8b9ef4391073b10cd84821485 (patch)
tree6be12440f93fd023a751af1de4a056f3acf3307f /net/base/cookie_monster.cc
parent61855fc2c781be3419e159e959078670ec6a6483 (diff)
downloadchromium_src-7a964a7f1edad8c8b9ef4391073b10cd84821485.zip
chromium_src-7a964a7f1edad8c8b9ef4391073b10cd84821485.tar.gz
chromium_src-7a964a7f1edad8c8b9ef4391073b10cd84821485.tar.bz2
This CL changes our per-domain limits to be per-effective domain.
This matches FireFox behavior and is for denial of service protection when we go to FireFox expiry behavior (keeping cookies around indefinitely if they don't have a max-age and are being used); see issue (8850) for details. BUG=8850 TEST=net_unittests/net_perftests on Linux with CookieMonsterTest.* filter Review URL: http://codereview.chromium.org/3122013 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@58732 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/base/cookie_monster.cc')
-rw-r--r--net/base/cookie_monster.cc417
1 files changed, 286 insertions, 131 deletions
diff --git a/net/base/cookie_monster.cc b/net/base/cookie_monster.cc
index 262da6a..100e1f8 100644
--- a/net/base/cookie_monster.cc
+++ b/net/base/cookie_monster.cc
@@ -66,22 +66,20 @@
using base::Time;
using base::TimeDelta;
+using base::TimeTicks;
static const int kMinutesInTenYears = 10 * 365 * 24 * 60;
namespace net {
-namespace {
+// See comments at declaration of these variables in cookie_monster.h
+// for details.
+const size_t CookieMonster::kDomainMaxCookies = 180;
+const size_t CookieMonster::kDomainPurgeCookies = 30;
+const size_t CookieMonster::kMaxCookies = 3300;
+const size_t CookieMonster::kPurgeCookies = 300;
-// Cookie garbage collection thresholds. Based off of the Mozilla defaults.
-// It might seem scary to have a high purge value, but really it's not. You
-// just make sure that you increase the max to cover the increase in purge,
-// and we would have been purging the same amount of cookies. We're just
-// going through the garbage collection process less often.
-const size_t kNumCookiesPerHost = 70; // ~50 cookies
-const size_t kNumCookiesPerHostPurge = 20;
-const size_t kNumCookiesTotal = 3300; // ~3000 cookies
-const size_t kNumCookiesTotalPurge = 300;
+namespace {
// Default minimum delay after updating a cookie's LastAccessDate before we
// will update it again.
@@ -108,6 +106,7 @@ void CookieMonster::EnableFileScheme() {
CookieMonster::CookieMonster(PersistentCookieStore* store, Delegate* delegate)
: initialized_(false),
+ use_effective_domain_key_scheme_(use_effective_domain_key_default_),
store_(store),
last_access_threshold_(
TimeDelta::FromSeconds(kDefaultAccessUpdateThresholdSeconds)),
@@ -158,9 +157,18 @@ void CookieMonster::InitializeHistograms() {
"net.CookieEvictedLastAccessMinutes",
1, kMinutesInTenYears, 50,
Histogram::kUmaTargetedHistogramFlag);
- histogram_count_ = Histogram::FactoryGet(
+ histogram_count_ = Histogram::FactoryGet(
"net.CookieCount", 1, 4000, 50,
Histogram::kUmaTargetedHistogramFlag);
+ histogram_domain_count_ = Histogram::FactoryGet(
+ "net.CookieDomainCount", 1, 4000, 50,
+ Histogram::kUmaTargetedHistogramFlag);
+ histogram_etldp1_count_ = Histogram::FactoryGet(
+ "net.CookieEtldp1Count", 1, 4000, 50,
+ Histogram::kUmaTargetedHistogramFlag);
+ histogram_domain_per_etldp1_count_ = Histogram::FactoryGet(
+ "net.CookieDomainPerEtldp1Count", 1, 4000, 50,
+ Histogram::kUmaTargetedHistogramFlag);
// From UMA_HISTOGRAM_COUNTS_10000 & UMA_HISTOGRAM_CUSTOM_COUNTS
histogram_number_duplicate_db_cookies_ = Histogram::FactoryGet(
@@ -172,18 +180,28 @@ void CookieMonster::InitializeHistograms() {
"net.CookieDeletionCause", 1,
DELETE_COOKIE_LAST_ENTRY, DELETE_COOKIE_LAST_ENTRY + 1,
Histogram::kUmaTargetedHistogramFlag);
+
+ // From UMA_HISTOGRAM_{CUSTOM_,}TIMES
+ histogram_time_get_ = Histogram::FactoryTimeGet("net.CookieTimeGet",
+ base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1),
+ 50, Histogram::kUmaTargetedHistogramFlag);
+ histogram_time_load_ = Histogram::FactoryTimeGet("net.CookieTimeLoad",
+ base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1),
+ 50, Histogram::kUmaTargetedHistogramFlag);
}
void CookieMonster::InitStore() {
DCHECK(store_) << "Store must exist to initialize";
+ TimeTicks beginning_time(TimeTicks::Now());
+
// Initialize the store and sync in any saved persistent cookies. We don't
// care if it's expired, insert it so it can be garbage collected, removed,
// and sync'd.
std::vector<CanonicalCookie*> cookies;
// Reserve space for the maximum amount of cookies a database should have.
// This prevents multiple vector growth / copies as we append cookies.
- cookies.reserve(kNumCookiesTotal);
+ cookies.reserve(kMaxCookies);
store_->Load(&cookies);
// Avoid ever letting cookies with duplicate creation times into the store;
@@ -195,7 +213,7 @@ void CookieMonster::InitStore() {
int64 cookie_creation_time = (*it)->CreationDate().ToInternalValue();
if (creation_times.insert(cookie_creation_time).second) {
- InternalInsertCookie((*it)->Domain(), *it, false);
+ InternalInsertCookie(GetKey((*it)->Domain()), *it, false);
} else {
LOG(ERROR) << StringPrintf("Found cookies with duplicate creation "
"times in backing store: "
@@ -214,6 +232,8 @@ void CookieMonster::InitStore() {
//
// In particular, the backing store might have given us duplicate cookies.
EnsureCookiesMapIsValid();
+
+ histogram_time_load_->AddTime(TimeTicks::Now() - beginning_time);
}
void CookieMonster::EnsureCookiesMapIsValid() {
@@ -231,7 +251,7 @@ void CookieMonster::EnsureCookiesMapIsValid() {
// Ensure no equivalent cookies for this host.
num_duplicates_trimmed +=
- TrimDuplicateCookiesForHost(key, cur_range_begin, cur_range_end);
+ TrimDuplicateCookiesForKey(key, cur_range_begin, cur_range_end);
}
// Record how many duplicates were found in the database.
@@ -278,7 +298,7 @@ struct CookieSignature {
};
}
-int CookieMonster::TrimDuplicateCookiesForHost(
+int CookieMonster::TrimDuplicateCookiesForKey(
const std::string& key,
CookieMap::iterator begin,
CookieMap::iterator end) {
@@ -302,15 +322,15 @@ int CookieMonster::TrimDuplicateCookiesForHost(
CookieSignature signature(cookie->Name(), cookie->Domain(),
cookie->Path());
- CookieSet& list = equivalent_cookies[signature];
+ CookieSet& set = equivalent_cookies[signature];
// We found a duplicate!
- if (!list.empty())
+ if (!set.empty())
num_duplicates++;
// We save the iterator into |cookies_| rather than the actual cookie
// pointer, since we may need to delete it later.
- bool insert_success = list.insert(it).second;
+ bool insert_success = set.insert(it).second;
DCHECK(insert_success) <<
"Duplicate creation times found in duplicate cookie name scan.";
}
@@ -368,6 +388,11 @@ void CookieMonster::SetDefaultCookieableSchemes() {
SetCookieableSchemes(kDefaultCookieableSchemes, num_schemes);
}
+void CookieMonster::SetKeyScheme(bool use_effective_domain_key) {
+ DCHECK(!initialized_);
+ use_effective_domain_key_scheme_ = use_effective_domain_key;
+}
+
// The system resolution is not high enough, so we can have multiple
// set cookies that result in the same system time. When this happens, we
// increment by one Time unit. Let's hope computers don't get too fast.
@@ -500,7 +525,7 @@ bool CookieMonster::DomainIsHostOnly(const std::string& domain_string) {
// Returns the effective TLD+1 for a given host. This only makes sense for http
// and https schemes. For other schemes, the host will be returned unchanged
-// (minus any leading .).
+// (minus any leading period).
static std::string GetEffectiveDomain(const std::string& scheme,
const std::string& host) {
if (scheme == "http" || scheme == "https")
@@ -511,14 +536,51 @@ static std::string GetEffectiveDomain(const std::string& scheme,
return host;
}
-// Determine the cookie domain key to use for setting a cookie with the
-// specified domain attribute string.
-// On success returns true, and sets cookie_domain_key to either a
-// -host cookie key (ex: "google.com")
-// -domain cookie key (ex: ".google.com")
-static bool GetCookieDomainKeyWithString(const GURL& url,
- const std::string& domain_string,
- std::string* cookie_domain_key) {
+// A wrapper around RegistryControlledDomainService::GetDomainAndRegistry
+// to make clear we're creating a key for our local map. Here and
+// in FindCookiesForHostAndDomain() are the only two places where
+// we need to conditionalize based on key type.
+//
+// Note that this key algorithm explicitly ignores the scheme. This is
+// because when we're entering cookies into the map from the backing store,
+// we in general won't have the scheme at that point.
+// In practical terms, this means that file cookies will be stored
+// in the map either by an empty string or by UNC name (and will be
+// limited by kMaxCookiesPerHost), and extension cookies will be stored
+// based on the single extension id, as the extension id won't have the
+// form of a DNS host and hence GetKey() will return it unchanged.
+//
+// Arguably the right thing to do here is to make the key
+// algorithm dependent on the scheme, and make sure that the scheme is
+// available everywhere the key must be obtained (specfically at backing
+// store load time). This would require either changing the backing store
+// database schema to include the scheme (far more trouble than it's worth), or
+// separating out file cookies into their own CookieMonster instance and
+// thus restricting each scheme to a single cookie monster (which might
+// be worth it, but is still too much trouble to solve what is currently a
+// non-problem).
+std::string CookieMonster::GetKey(const std::string& domain) const {
+ if (!use_effective_domain_key_scheme_)
+ return domain;
+
+ std::string effective_domain(
+ RegistryControlledDomainService::GetDomainAndRegistry(domain));
+ if (effective_domain.empty())
+ effective_domain = domain;
+
+ if (!effective_domain.empty() && effective_domain[0] == '.')
+ return effective_domain.substr(1);
+ return effective_domain;
+}
+
+// Determine the actual cookie domain based on the domain string passed
+// (if any) and the URL from which the cookie came.
+// On success returns true, and sets cookie_domain to either a
+// -host cookie domain (ex: "google.com")
+// -domain cookie domain (ex: ".google.com")
+static bool GetCookieDomainWithString(const GURL& url,
+ const std::string& domain_string,
+ std::string* result) {
const std::string url_host(url.host());
// If no domain was specified in the domain string, default to a host cookie.
@@ -526,8 +588,8 @@ static bool GetCookieDomainKeyWithString(const GURL& url,
// ip address hostname exactly. It should be treated as a host cookie.
if (domain_string.empty() ||
(url.HostIsIPAddress() && url_host == domain_string)) {
- *cookie_domain_key = url_host;
- DCHECK(CookieMonster::DomainIsHostOnly(*cookie_domain_key));
+ *result = url_host;
+ DCHECK(CookieMonster::DomainIsHostOnly(*result));
return true;
}
@@ -565,18 +627,18 @@ static bool GetCookieDomainKeyWithString(const GURL& url,
cookie_domain.length(), cookie_domain))
return false;
- *cookie_domain_key = cookie_domain;
+ *result = cookie_domain;
return true;
}
-// Determine the cookie domain key to use for setting the specified cookie.
-static bool GetCookieDomainKey(const GURL& url,
- const CookieMonster::ParsedCookie& pc,
- std::string* cookie_domain_key) {
+// Determine the cookie domain to use for setting the specified cookie.
+static bool GetCookieDomain(const GURL& url,
+ const CookieMonster::ParsedCookie& pc,
+ std::string* result) {
std::string domain_string;
if (pc.HasDomain())
domain_string = pc.Domain();
- return GetCookieDomainKeyWithString(url, domain_string, cookie_domain_key);
+ return GetCookieDomainWithString(url, domain_string, result);
}
static std::string CanonPathWithString(const GURL& url,
@@ -701,7 +763,7 @@ bool CookieMonster::SetCookieWithCreationTimeAndOptions(
}
std::string cookie_domain;
- if (!GetCookieDomainKey(url, pc, &cookie_domain)) {
+ if (!GetCookieDomain(url, pc, &cookie_domain)) {
return false;
}
@@ -769,13 +831,14 @@ bool CookieMonster::SetCookieWithDetails(
bool CookieMonster::SetCanonicalCookie(scoped_ptr<CanonicalCookie>* cc,
const Time& creation_time,
const CookieOptions& options) {
- const std::string domain((*cc)->Domain());
- if (DeleteAnyEquivalentCookie(domain, **cc, options.exclude_httponly())) {
+ const std::string key(GetKey((*cc)->Domain()));
+ if (DeleteAnyEquivalentCookie(key, **cc, options.exclude_httponly())) {
COOKIE_DLOG(INFO) << "SetCookie() not clobbering httponly cookie";
return false;
}
- COOKIE_DLOG(INFO) << "SetCookie() cc: " << (*cc)->DebugString();
+ COOKIE_DLOG(INFO) << "SetCookie() key: " << key
+ << " cc: " << (*cc)->DebugString();
// Realize that we might be setting an expired cookie, and the only point
// was to delete the cookie which we've already done.
@@ -783,7 +846,7 @@ bool CookieMonster::SetCanonicalCookie(scoped_ptr<CanonicalCookie>* cc,
// See InitializeHistograms() for details.
histogram_expiration_duration_minutes_->Add(
((*cc)->ExpiryDate() - creation_time).InMinutes());
- InternalInsertCookie(domain, cc->release(), true);
+ InternalInsertCookie(key, cc->release(), true);
}
// We assume that hopefully setting a cookie will be less common than
@@ -791,7 +854,7 @@ bool CookieMonster::SetCanonicalCookie(scoped_ptr<CanonicalCookie>* cc,
// make sure that we garbage collect... We can also make the assumption that
// if a cookie was set, in the common case it will be used soon after,
// and we will purge the expired cookies in GetCookies().
- GarbageCollect(creation_time, domain);
+ GarbageCollect(creation_time, key);
return true;
}
@@ -802,20 +865,20 @@ void CookieMonster::InternalInsertCookie(const std::string& key,
lock_.AssertAcquired();
if (cc->IsPersistent() && store_ && sync_to_store)
- store_->AddCookie(key, *cc);
+ store_->AddCookie(*cc);
cookies_.insert(CookieMap::value_type(key, cc));
if (delegate_.get())
delegate_->OnCookieChanged(*cc, false);
}
-void CookieMonster::InternalUpdateCookieAccessTime(CanonicalCookie* cc) {
+void CookieMonster::InternalUpdateCookieAccessTime(CanonicalCookie* cc,
+ const Time& current) {
lock_.AssertAcquired();
// Based off the Mozilla code. When a cookie has been accessed recently,
// don't bother updating its access time again. This reduces the number of
// updates we do during pageload, which in turn reduces the chance our storage
// backend will hit its batch thresholds and be forced to update.
- const Time current = Time::Now();
if ((current - cc->LastAccessDate()) < last_access_threshold_)
return;
@@ -834,10 +897,12 @@ void CookieMonster::InternalDeleteCookie(CookieMap::iterator it,
lock_.AssertAcquired();
// See InitializeHistograms() for details.
- histogram_cookie_deletion_cause_->Add(deletion_cause);
+ if (deletion_cause != DELETE_COOKIE_DONT_RECORD)
+ histogram_cookie_deletion_cause_->Add(deletion_cause);
CanonicalCookie* cc = it->second;
COOKIE_DLOG(INFO) << "InternalDeleteCookie() cc: " << cc->DebugString();
+
if (cc->IsPersistent() && store_ && sync_to_store)
store_->DeleteCookie(*cc);
if (delegate_.get())
@@ -882,18 +947,27 @@ int CookieMonster::GarbageCollect(const Time& current,
int num_deleted = 0;
// Collect garbage for this key.
- if (cookies_.count(key) > kNumCookiesPerHost) {
+ if (cookies_.count(key) > kDomainMaxCookies) {
COOKIE_DLOG(INFO) << "GarbageCollect() key: " << key;
- num_deleted += GarbageCollectRange(current, cookies_.equal_range(key),
- kNumCookiesPerHost, kNumCookiesPerHostPurge);
+
+ std::vector<CookieMap::iterator> cookie_its;
+ num_deleted += GarbageCollectExpired(current, cookies_.equal_range(key),
+ &cookie_its);
+ num_deleted += GarbageCollectEvict(
+ current, kDomainMaxCookies, kDomainPurgeCookies,
+ DELETE_COOKIE_EVICTED_DOMAIN, &cookie_its);
}
// Collect garbage for everything.
- if (cookies_.size() > kNumCookiesTotal) {
+ if (cookies_.size() > kMaxCookies) {
COOKIE_DLOG(INFO) << "GarbageCollect() everything";
- num_deleted += GarbageCollectRange(current,
- CookieMapItPair(cookies_.begin(), cookies_.end()), kNumCookiesTotal,
- kNumCookiesTotalPurge);
+ std::vector<CookieMap::iterator> cookie_its;
+ num_deleted += GarbageCollectExpired(
+ current, CookieMapItPair(cookies_.begin(), cookies_.end()),
+ &cookie_its);
+ num_deleted += GarbageCollectEvict(
+ current, kMaxCookies, kPurgeCookies,
+ DELETE_COOKIE_EVICTED_GLOBAL, &cookie_its);
}
return num_deleted;
@@ -911,38 +985,6 @@ static bool LRUCookieSorter(const CookieMonster::CookieMap::iterator& it1,
return it1->second->CreationDate() < it2->second->CreationDate();
}
-int CookieMonster::GarbageCollectRange(const Time& current,
- const CookieMapItPair& itpair,
- size_t num_max,
- size_t num_purge) {
- lock_.AssertAcquired();
-
- // First, delete anything that's expired.
- std::vector<CookieMap::iterator> cookie_its;
- int num_deleted = GarbageCollectExpired(current, itpair, &cookie_its);
-
- // If the range still has too many cookies, delete the least recently used.
- if (cookie_its.size() > num_max) {
- COOKIE_DLOG(INFO) << "GarbageCollectRange() Deep Garbage Collect.";
- // Purge down to (|num_max| - |num_purge|) total cookies.
- DCHECK(num_purge <= num_max);
- num_purge += cookie_its.size() - num_max;
-
- std::partial_sort(cookie_its.begin(), cookie_its.begin() + num_purge,
- cookie_its.end(), LRUCookieSorter);
- for (size_t i = 0; i < num_purge; ++i) {
- // See InitializeHistograms() for details.
- histogram_evicted_last_access_minutes_->Add(
- (current - cookie_its[i]->second->LastAccessDate()).InMinutes());
- InternalDeleteCookie(cookie_its[i], true, DELETE_COOKIE_EVICTED);
- }
-
- num_deleted += num_purge;
- }
-
- return num_deleted;
-}
-
int CookieMonster::GarbageCollectExpired(
const Time& current,
const CookieMapItPair& itpair,
@@ -965,6 +1007,32 @@ int CookieMonster::GarbageCollectExpired(
return num_deleted;
}
+int CookieMonster::GarbageCollectEvict(
+ const Time& current,
+ size_t num_max,
+ size_t num_purge,
+ DeletionCause cause,
+ std::vector<CookieMap::iterator>* cookie_its) {
+ if (cookie_its->size() <= num_max) {
+ return 0;
+ }
+
+ COOKIE_DLOG(INFO) << "GarbageCollectEvict() Deep Garbage Collect.";
+ // Purge down to (|num_max| - |num_purge|) total cookies.
+ DCHECK_LE(num_purge, num_max);
+ num_purge += cookie_its->size() - num_max;
+
+ std::partial_sort(cookie_its->begin(), cookie_its->begin() + num_purge,
+ cookie_its->end(), LRUCookieSorter);
+ for (size_t i = 0; i < num_purge; ++i) {
+ // See InitializeHistograms() for details.
+ histogram_evicted_last_access_minutes_->Add(
+ (current - (*cookie_its)[i]->second->LastAccessDate()).InMinutes());
+ InternalDeleteCookie((*cookie_its)[i], true, cause);
+ }
+ return num_purge;
+}
+
int CookieMonster::DeleteAll(bool sync_to_store) {
AutoLock autolock(lock_);
InitIfNecessary();
@@ -1016,17 +1084,26 @@ int CookieMonster::DeleteAllForHost(const GURL& url) {
if (!HasCookieableScheme(url))
return 0;
+ const std::string scheme(url.scheme());
+ const std::string host(url.host());
+
// We store host cookies in the store by their canonical host name;
// domain cookies are stored with a leading ".". So this is a pretty
// simple lookup and per-cookie delete.
int num_deleted = 0;
- for (CookieMapItPair its = cookies_.equal_range(url.host());
+ for (CookieMapItPair its = cookies_.equal_range(GetKey(host));
its.first != its.second;) {
CookieMap::iterator curit = its.first;
++its.first;
- num_deleted++;
- InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPLICIT);
+ const CanonicalCookie* const cc = curit->second;
+
+ // Delete only on a match as a host cookie.
+ if (cc->IsHostCookie() && cc->IsDomainMatch(scheme, host)) {
+ num_deleted++;
+
+ InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPLICIT);
+ }
}
return num_deleted;
}
@@ -1035,7 +1112,7 @@ bool CookieMonster::DeleteCanonicalCookie(const CanonicalCookie& cookie) {
AutoLock autolock(lock_);
InitIfNecessary();
- for (CookieMapItPair its = cookies_.equal_range(cookie.Domain());
+ for (CookieMapItPair its = cookies_.equal_range(GetKey(cookie.Domain()));
its.first != its.second; ++its.first) {
// The creation date acts as our unique index...
if (its.first->second->CreationDate() == cookie.CreationDate()) {
@@ -1070,18 +1147,6 @@ bool CookieMonster::SetCookieWithOptions(const GURL& url,
return SetCookieWithCreationTimeAndOptions(url, cookie_line, Time(), options);
}
-// Currently our cookie datastructure is based on Mozilla's approach. We have a
-// hash keyed on the cookie's domain, and for any query we walk down the domain
-// components and probe for cookies until we reach the TLD, where we stop.
-// For example, a.b.blah.com, we would probe
-// - a.b.blah.com
-// - .a.b.blah.com (TODO should we check this first or second?)
-// - .b.blah.com
-// - .blah.com
-// There are some alternative datastructures we could try, like a
-// search/prefix trie, where we reverse the hostname and query for all
-// keys that are a prefix of our hostname. I think the hash probing
-// should be fast and simple enough for now.
std::string CookieMonster::GetCookiesWithOptions(const GURL& url,
const CookieOptions& options) {
AutoLock autolock(lock_);
@@ -1091,6 +1156,8 @@ std::string CookieMonster::GetCookiesWithOptions(const GURL& url,
return std::string();
}
+ TimeTicks start_time(TimeTicks::Now());
+
// Get the cookies for this host and its domain(s).
std::vector<CanonicalCookie*> cookies;
FindCookiesForHostAndDomain(url, options, true, &cookies);
@@ -1109,6 +1176,8 @@ std::string CookieMonster::GetCookiesWithOptions(const GURL& url,
cookie_line += (*it)->Value();
}
+ histogram_time_get_->AddTime(TimeTicks::Now() - start_time);
+
COOKIE_DLOG(INFO) << "GetCookies() result: " << cookie_line;
return cookie_line;
@@ -1193,9 +1262,9 @@ CookieMonster::CookieList CookieMonster::GetAllCookiesForURL(const GURL& url) {
CookieList cookies;
for (std::vector<CanonicalCookie*>::const_iterator it = cookie_ptrs.begin();
- it != cookie_ptrs.end(); it++) {
+ it != cookie_ptrs.end(); it++)
cookies.push_back(**it);
- }
+
return cookies;
}
@@ -1213,29 +1282,39 @@ void CookieMonster::FindCookiesForHostAndDomain(
// want to collect statistics whenever the browser's being used.
RecordPeriodicStats(current_time);
- // Query for the full host, For example: 'a.c.blah.com'.
- std::string key(url.host());
- FindCookiesForKey(key, url, options, current_time, update_access_time,
- cookies);
-
- // See if we can search for domain cookies, i.e. if the host has a TLD + 1.
- const std::string domain(GetEffectiveDomain(url.scheme(), key));
- if (domain.empty())
- return;
- DCHECK_LE(domain.length(), key.length());
- DCHECK_EQ(0, key.compare(key.length() - domain.length(), domain.length(),
- domain));
-
- // Walk through the string and query at the dot points (GURL should have
- // canonicalized the dots, so this should be safe). Stop once we reach the
- // domain + registry; we can't write cookies past this point, and with some
- // registrars other domains can, in which case we don't want to read their
- // cookies.
- for (key = "." + key; key.length() > domain.length(); ) {
+ if (use_effective_domain_key_scheme_) {
+ // Can just dispatch to FindCookiesForKey
+ const std::string key(GetKey(url.host()));
+ FindCookiesForKey(key, url, options, current_time,
+ update_access_time, cookies);
+ } else {
+ // Need to probe for all domains that might have relevant
+ // cookies for us.
+
+ // Query for the full host, For example: 'a.c.blah.com'.
+ std::string key(GetKey(url.host()));
FindCookiesForKey(key, url, options, current_time, update_access_time,
cookies);
- const size_t next_dot = key.find('.', 1); // Skip over leading dot.
- key.erase(0, next_dot);
+
+ // See if we can search for domain cookies, i.e. if the host has a TLD + 1.
+ const std::string domain(GetEffectiveDomain(url.scheme(), key));
+ if (domain.empty())
+ return;
+ DCHECK_LE(domain.length(), key.length());
+ DCHECK_EQ(0, key.compare(key.length() - domain.length(), domain.length(),
+ domain));
+
+ // Walk through the string and query at the dot points (GURL should have
+ // canonicalized the dots, so this should be safe). Stop once we reach the
+ // domain + registry; we can't write cookies past this point, and with some
+ // registrars other domains can, in which case we don't want to read their
+ // cookies.
+ for (key = "." + key; key.length() > domain.length(); ) {
+ FindCookiesForKey(key, url, options, current_time, update_access_time,
+ cookies);
+ const size_t next_dot = key.find('.', 1); // Skip over leading dot.
+ key.erase(0, next_dot);
+ }
}
}
@@ -1248,6 +1327,8 @@ void CookieMonster::FindCookiesForKey(
std::vector<CanonicalCookie*>* cookies) {
lock_.AssertAcquired();
+ const std::string scheme(url.scheme());
+ const std::string host(url.host());
bool secure = url.SchemeIsSecure();
for (CookieMapItPair its = cookies_.equal_range(key);
@@ -1270,13 +1351,17 @@ void CookieMonster::FindCookiesForKey(
if (!secure && cc->IsSecure())
continue;
+ // Filter out cookies that don't apply to this domain.
+ if (use_effective_domain_key_scheme_ && !cc->IsDomainMatch(scheme, host))
+ continue;
+
if (!cc->IsOnPath(url.path()))
continue;
// Add this cookie to the set of matching cookies. Update the access
// time if we've been requested to do so.
if (update_access_time) {
- InternalUpdateCookieAccessTime(cc);
+ InternalUpdateCookieAccessTime(cc, current);
}
cookies->push_back(cc);
}
@@ -1294,12 +1379,47 @@ void CookieMonster::RecordPeriodicStats(const base::Time& current_time) {
const base::TimeDelta kRecordStatisticsIntervalTime(
base::TimeDelta::FromSeconds(kRecordStatisticsIntervalSeconds));
- if (current_time - last_statistic_record_time_ >
+ // If we've taken statistics recently, return.
+ if (current_time - last_statistic_record_time_ <=
kRecordStatisticsIntervalTime) {
- // See InitializeHistograms() for details.
- histogram_count_->Add(cookies_.size());
- last_statistic_record_time_ = current_time;
+ return;
+ }
+
+ // See InitializeHistograms() for details.
+ histogram_count_->Add(cookies_.size());
+
+ // More detailed statistics on cookie counts at different granularities.
+ TimeTicks beginning_of_time(TimeTicks::Now());
+
+ for (CookieMap::const_iterator it_key = cookies_.begin();
+ it_key != cookies_.end(); ) {
+ const std::string& key(it_key->first);
+
+ int key_count = 0;
+ typedef std::map<std::string, unsigned int> DomainMap;
+ DomainMap domain_map;
+ CookieMapItPair its_cookies = cookies_.equal_range(key);
+ while (its_cookies.first != its_cookies.second) {
+ key_count++;
+ const std::string& cookie_domain(its_cookies.first->second->Domain());
+ domain_map[cookie_domain]++;
+
+ its_cookies.first++;
+ }
+ histogram_etldp1_count_->Add(key_count);
+ histogram_domain_per_etldp1_count_->Add(domain_map.size());
+ for (DomainMap::const_iterator domain_map_it = domain_map.begin();
+ domain_map_it != domain_map.end(); domain_map_it++)
+ histogram_domain_count_->Add(domain_map_it->second);
+
+ it_key = its_cookies.second;
}
+
+ DLOG(INFO) << "Time for recording cookie stats (us): "
+ << (TimeTicks::Now() - beginning_of_time).InMicroseconds()
+ << std::endl;
+
+ last_statistic_record_time_ = current_time;
}
CookieMonster::ParsedCookie::ParsedCookie(const std::string& cookie_line)
@@ -1597,8 +1717,8 @@ CookieMonster::CanonicalCookie::CanonicalCookie(const GURL& url,
domain_string = pc.Domain();
}
bool result
- = GetCookieDomainKeyWithString(url, domain_string,
- &cookie_domain);
+ = GetCookieDomainWithString(url, domain_string,
+ &cookie_domain);
// Caller is responsible for passing in good arguments.
DCHECK(result);
domain_ = cookie_domain;
@@ -1622,7 +1742,7 @@ CookieMonster::CanonicalCookie* CookieMonster::CanonicalCookie::Create(
if (parsed_domain != domain)
return NULL;
std::string cookie_domain;
- if (!GetCookieDomainKeyWithString(url, parsed_domain, &cookie_domain))
+ if (!GetCookieDomainWithString(url, parsed_domain, &cookie_domain))
return NULL;
std::string parsed_path = ParsedCookie::ParseValueString(path);
@@ -1684,6 +1804,41 @@ bool CookieMonster::CanonicalCookie::IsOnPath(
return true;
}
+bool CookieMonster::CanonicalCookie::IsDomainMatch(
+ const std::string& scheme,
+ const std::string& host) const {
+ // Can domain match in two ways; as a domain cookie (where the cookie
+ // domain begins with ".") or as a host cookie (where it doesn't).
+
+ // Some consumers of the CookieMonster expect to set cookies on
+ // URLs like http://.strange.url. To retrieve cookies in this instance,
+ // we allow matching as a host cookie even when the domain_ starts with
+ // a period.
+ if (host == domain_)
+ return true;
+
+ // Domain cookie must have an initial ".". To match, it must be
+ // equal to url's host with initial period removed, or a suffix of
+ // it.
+
+ // Arguably this should only apply to "http" or "https" cookies, but
+ // extension cookie tests currently use the funtionality, and if we
+ // ever decide to implement that it should be done by preventing
+ // such cookies from being set.
+ if (domain_.empty() || domain_[0] != '.')
+ return false;
+
+ // The host with a "." prefixed.
+ if (domain_.compare(1, std::string::npos, host) == 0)
+ return true;
+
+ // A pure suffix of the host (ok since we know the domain already
+ // starts with a ".")
+ return (host.length() > domain_.length() &&
+ host.compare(host.length() - domain_.length(),
+ domain_.length(), domain_) == 0);
+}
+
std::string CookieMonster::CanonicalCookie::DebugString() const {
return StringPrintf("name: %s value: %s domain: %s path: %s creation: %"
PRId64,