summaryrefslogtreecommitdiffstats
path: root/net/base/cookie_monster.h
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2010-07-29 17:14:53 +0100
committerBen Murdoch <benm@google.com>2010-08-04 14:29:45 +0100
commitc407dc5cd9bdc5668497f21b26b09d988ab439de (patch)
tree7eaf8707c0309516bdb042ad976feedaf72b0bb1 /net/base/cookie_monster.h
parent0998b1cdac5733f299c12d88bc31ef9c8035b8fa (diff)
downloadexternal_chromium-c407dc5cd9bdc5668497f21b26b09d988ab439de.zip
external_chromium-c407dc5cd9bdc5668497f21b26b09d988ab439de.tar.gz
external_chromium-c407dc5cd9bdc5668497f21b26b09d988ab439de.tar.bz2
Merge Chromium src@r53293
Change-Id: Ia79acf8670f385cee48c45b0a75371d8e950af34
Diffstat (limited to 'net/base/cookie_monster.h')
-rw-r--r--net/base/cookie_monster.h329
1 files changed, 248 insertions, 81 deletions
diff --git a/net/base/cookie_monster.h b/net/base/cookie_monster.h
index 7578c46..623363f 100644
--- a/net/base/cookie_monster.h
+++ b/net/base/cookie_monster.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -13,7 +13,10 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/histogram.h"
#include "base/lock.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
#include "base/time.h"
#include "net/base/cookie_store.h"
@@ -33,8 +36,9 @@ namespace net {
// - Verify that our domain enforcement and non-dotted handling is correct
class CookieMonster : public CookieStore {
public:
- class ParsedCookie;
class CanonicalCookie;
+ class Delegate;
+ class ParsedCookie;
class PersistentCookieStore;
// NOTE(deanm):
@@ -47,24 +51,27 @@ class CookieMonster : public CookieStore {
typedef std::multimap<std::string, CanonicalCookie*> CookieMap;
typedef std::pair<CookieMap::iterator, CookieMap::iterator> CookieMapItPair;
typedef std::pair<std::string, CanonicalCookie*> KeyedCanonicalCookie;
- typedef std::pair<std::string, CanonicalCookie> CookieListPair;
- typedef std::vector<CookieListPair> CookieList;
-
+ typedef std::vector<CanonicalCookie> CookieList;
- CookieMonster();
-
- // The store passed in should not have had Init() called on it yet. This class
- // will take care of initializing it. The backing store is NOT owned by this
- // class, but it must remain valid for the duration of the cookie monster's
- // existence.
- CookieMonster(PersistentCookieStore* store);
+ // The store passed in should not have had Init() called on it yet. This
+ // class will take care of initializing it. The backing store is NOT owned by
+ // this class, but it must remain valid for the duration of the cookie
+ // monster's existence. If |store| is NULL, then no backing store will be
+ // updated. If |delegate| is non-NULL, it will be notified on
+ // creation/deletion of cookies.
+ CookieMonster(PersistentCookieStore* store, Delegate* delegate);
#ifdef UNIT_TEST
- CookieMonster(int last_access_threshold_milliseconds)
+ CookieMonster(PersistentCookieStore* store,
+ Delegate* delegate,
+ int last_access_threshold_milliseconds)
: initialized_(false),
- store_(NULL),
+ store_(store),
last_access_threshold_(base::TimeDelta::FromMilliseconds(
- last_access_threshold_milliseconds)) {
+ last_access_threshold_milliseconds)),
+ delegate_(delegate),
+ last_statistic_record_time_(base::Time::Now()) {
+ InitializeHistograms();
SetDefaultCookieableSchemes();
}
#endif
@@ -72,6 +79,10 @@ class CookieMonster : public CookieStore {
// Parse the string with the cookie time (very forgivingly).
static base::Time ParseCookieTime(const std::string& time_string);
+ // Returns true if a domain string represents a host-only cookie,
+ // i.e. it doesn't begin with a leading '.' character.
+ static bool DomainIsHostOnly(const std::string& domain_string);
+
// CookieStore implementation.
virtual bool SetCookieWithOptions(const GURL& url,
const std::string& cookie_line,
@@ -81,6 +92,19 @@ class CookieMonster : public CookieStore {
virtual void DeleteCookie(const GURL& url, const std::string& cookie_name);
virtual CookieMonster* GetCookieMonster() { return this; }
+ // Sets a cookie given explicit user-provided cookie attributes. The cookie
+ // name, value, domain, etc. are each provided as separate strings. This
+ // function expects each attribute to be well-formed. It will check for
+ // disallowed characters (e.g. the ';' character is disallowed within the
+ // cookie value attribute) and will return false without setting the cookie
+ // if such characters are found.
+ bool SetCookieWithDetails(const GURL& url,
+ const std::string& name,
+ const std::string& value,
+ const std::string& domain,
+ const std::string& path,
+ const base::Time& expiration_time,
+ bool secure, bool http_only);
// Exposed for unit testing.
bool SetCookieWithCreationTimeAndOptions(const GURL& url,
@@ -99,8 +123,8 @@ class CookieMonster : public CookieStore {
CookieList GetAllCookies();
// Returns all the cookies, for use in management UI, etc. Filters results
- // using given url scheme and host / domain. This does not mark the cookies
- // as having been accessed.
+ // using given url scheme, host / domain and path. This does not mark the
+ // cookies as having been accessed.
CookieList GetAllCookiesForURL(const GURL& url);
// Delete all of the cookies.
@@ -114,6 +138,12 @@ class CookieMonster : public CookieStore {
// one passed into the function via |delete_after|.
int DeleteAllCreatedAfter(const base::Time& delete_begin, bool sync_to_store);
+ // Delete all cookies that match the host of the given URL
+ // regardless of path. This includes all http_only and secure cookies,
+ // but does not include any domain cookies that may apply to this host.
+ // Returns the number of cookies deleted.
+ int DeleteAllForHost(const GURL& url);
+
// Delete one specific cookie.
bool DeleteCookie(const std::string& domain,
const CanonicalCookie& cookie,
@@ -122,6 +152,8 @@ class CookieMonster : public CookieStore {
// Override the default list of schemes that are allowed to be set in
// this cookie store. Calling his overrides the value of
// "enable_file_scheme_".
+ // If this this method is called, it must be called before first use of
+ // the instance (i.e. as part of the instance initialization process.)
void SetCookieableSchemes(const char* schemes[], size_t num_schemes);
// There are some unknowns about how to correctly handle file:// cookies,
@@ -150,6 +182,17 @@ class CookieMonster : public CookieStore {
// Should only be called by InitIfNecessary().
void InitStore();
+ // Checks that |cookies_| matches our invariants, and tries to repair any
+ // inconsistencies. (In other words, it does not have duplicate cookies).
+ void EnsureCookiesMapIsValid();
+
+ // Checks for any duplicate cookies for host |key|, which lie between
+ // |begin| and |end|. If any are found, all but the most recent are deleted.
+ // Returns the number of duplicate cookies that were deleted.
+ int TrimDuplicateCookiesForHost(const std::string& key,
+ CookieMap::iterator begin,
+ CookieMap::iterator end);
+
void SetDefaultCookieableSchemes();
void FindCookiesForHostAndDomain(const GURL& url,
@@ -164,8 +207,13 @@ class CookieMonster : public CookieStore {
void FindRawCookies(const std::string& key,
bool include_secure,
+ const std::string& path,
CookieList* list);
+ // Internal helper returning all cookies for a given URL. The caller is
+ // assumed to hold lock_ and having called InitIfNecessary().
+ CookieList InternalGetAllCookiesForURL(const GURL& url);
+
// Delete any cookies that are equivalent to |ecc| (same path, key, etc).
// If |skip_httponly| is true, httponly cookies will not be deleted. The
// return value with be true if |skip_httponly| skipped an httponly cookie.
@@ -178,9 +226,28 @@ class CookieMonster : public CookieStore {
CanonicalCookie* cc,
bool sync_to_store);
+ // Helper function that sets a canonical cookie, deleting equivalents and
+ // performing garbage collection.
+ bool SetCanonicalCookie(scoped_ptr<CanonicalCookie>* cc,
+ const std::string& cookie_domain,
+ const base::Time& creation_time,
+ const CookieOptions& options);
+
void InternalUpdateCookieAccessTime(CanonicalCookie* cc);
- void InternalDeleteCookie(CookieMap::iterator it, bool sync_to_store);
+ enum DeletionCause {
+ DELETE_COOKIE_EXPLICIT,
+ DELETE_COOKIE_OVERWRITE,
+ DELETE_COOKIE_EXPIRED,
+ DELETE_COOKIE_EVICTED,
+ DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE,
+ DELETE_COOKIE_DONT_RECORD,
+ DELETE_COOKIE_LAST_ENTRY = DELETE_COOKIE_DONT_RECORD
+ };
+
+ // |deletion_cause| argument is for collecting statistics.
+ void InternalDeleteCookie(CookieMap::iterator it, bool sync_to_store,
+ DeletionCause deletion_cause);
// If the number of cookies for host |key|, or globally, are over preset
// maximums, garbage collects, first for the host and then globally, as
@@ -212,6 +279,27 @@ class CookieMonster : public CookieStore {
bool HasCookieableScheme(const GURL& url);
+ // Statistics support
+ // Record statistics every kRecordStatisticsIntervalSeconds of uptime.
+ static const int kRecordStatisticsIntervalSeconds = 10 * 60;
+
+ // This function should be called repeatedly, and will record
+ // statistics if a sufficient time period has passed.
+ void RecordPeriodicStats(const base::Time& current_time);
+
+ // Histogram variables; see CookieMonster::InitializeHistograms() in
+ // cookie_monster.cc for details.
+ scoped_refptr<Histogram> histogram_expiration_duration_minutes_;
+ scoped_refptr<Histogram> histogram_between_access_interval_minutes_;
+ scoped_refptr<Histogram> histogram_evicted_last_access_minutes_;
+ scoped_refptr<Histogram> histogram_count_;
+ scoped_refptr<Histogram> histogram_number_duplicate_db_cookies_;
+ scoped_refptr<Histogram> histogram_cookie_deletion_cause_;
+
+ // Initialize the above variables; should only be called from
+ // the constructor.
+ void InitializeHistograms();
+
CookieMap cookies_;
// Indicates whether the cookie store has been initialized. This happens
@@ -231,77 +319,27 @@ class CookieMonster : public CookieStore {
std::vector<std::string> cookieable_schemes_;
+ scoped_refptr<Delegate> delegate_;
+
// Lock for thread-safety
Lock lock_;
- DISALLOW_COPY_AND_ASSIGN(CookieMonster);
-};
-
-class CookieMonster::ParsedCookie {
- public:
- typedef std::pair<std::string, std::string> TokenValuePair;
- typedef std::vector<TokenValuePair> PairList;
-
- // The maximum length of a cookie string we will try to parse
- static const size_t kMaxCookieSize = 4096;
- // The maximum number of Token/Value pairs. Shouldn't have more than 8.
- static const int kMaxPairs = 16;
-
- // Construct from a cookie string like "BLAH=1; path=/; domain=.google.com"
- ParsedCookie(const std::string& cookie_line);
- ~ParsedCookie() { }
-
- // You should not call any other methods on the class if !IsValid
- bool IsValid() const { return is_valid_; }
-
- const std::string& Name() const { return pairs_[0].first; }
- const std::string& Token() const { return Name(); }
- const std::string& Value() const { return pairs_[0].second; }
-
- bool HasPath() const { return path_index_ != 0; }
- const std::string& Path() const { return pairs_[path_index_].second; }
- bool HasDomain() const { return domain_index_ != 0; }
- const std::string& Domain() const { return pairs_[domain_index_].second; }
- bool HasExpires() const { return expires_index_ != 0; }
- const std::string& Expires() const { return pairs_[expires_index_].second; }
- bool HasMaxAge() const { return maxage_index_ != 0; }
- const std::string& MaxAge() const { return pairs_[maxage_index_].second; }
- bool IsSecure() const { return secure_index_ != 0; }
- bool IsHttpOnly() const { return httponly_index_ != 0; }
-
- // Return the number of attributes, for example, returning 2 for:
- // "BLAH=hah; path=/; domain=.google.com"
- size_t NumberOfAttributes() const { return pairs_.size() - 1; }
-
- // For debugging only!
- std::string DebugString() const;
-
- private:
- void ParseTokenValuePairs(const std::string& cookie_line);
- void SetupAttributes();
-
- PairList pairs_;
- bool is_valid_;
- // These will default to 0, but that should never be valid since the
- // 0th index is the user supplied token/value, not an attribute.
- // We're really never going to have more than like 8 attributes, so we
- // could fit these into 3 bits each if we're worried about size...
- size_t path_index_;
- size_t domain_index_;
- size_t expires_index_;
- size_t maxage_index_;
- size_t secure_index_;
- size_t httponly_index_;
+ base::Time last_statistic_record_time_;
- DISALLOW_COPY_AND_ASSIGN(ParsedCookie);
+ DISALLOW_COPY_AND_ASSIGN(CookieMonster);
};
-
class CookieMonster::CanonicalCookie {
public:
+
+ // These constructors do no validation or canonicalization of their inputs;
+ // the resulting CanonicalCookies should not be relied on to be canonical
+ // unless the caller has done appropriate validation and canonicalization
+ // themselves.
CanonicalCookie() { }
CanonicalCookie(const std::string& name,
const std::string& value,
+ const std::string& domain,
const std::string& path,
bool secure,
bool httponly,
@@ -311,6 +349,7 @@ class CookieMonster::CanonicalCookie {
const base::Time& expires)
: name_(name),
value_(value),
+ domain_(domain),
path_(path),
creation_date_(creation),
last_access_date_(last_access),
@@ -320,10 +359,25 @@ class CookieMonster::CanonicalCookie {
httponly_(httponly) {
}
+ // This constructor does canonicalization but not validation.
+ // The result of this constructor should not be relied on in contexts
+ // in which pre-validation of the ParsedCookie has not been done.
+ CanonicalCookie(const GURL& url, const ParsedCookie& pc);
+
// Supports the default copy constructor.
+ // Creates a canonical cookie from unparsed attribute values.
+ // Canonicalizes and validates inputs. May return NULL if an attribute
+ // value is invalid.
+ static CanonicalCookie* Create(
+ const GURL& url, const std::string& name, const std::string& value,
+ const std::string& domain, const std::string& path,
+ const base::Time& creation_time, const base::Time& expiration_time,
+ bool secure, bool http_only);
+
const std::string& Name() const { return name_; }
const std::string& Value() const { return value_; }
+ const std::string& Domain() const { return domain_; }
const std::string& Path() const { return path_; }
const base::Time& CreationDate() const { return creation_date_; }
const base::Time& LastAccessDate() const { return last_access_date_; }
@@ -337,12 +391,18 @@ class CookieMonster::CanonicalCookie {
return has_expires_ && current >= expiry_date_;
}
- // Are the cookies considered equivalent in the eyes of the RFC.
- // This says that the domain and path should string match identically.
+ // Are the cookies considered equivalent in the eyes of RFC 2965.
+ // The RFC says that name must match (case-sensitive), domain must
+ // match (case insensitive), and path must match (case sensitive).
+ // For the case insensitive domain compare, we rely on the domain
+ // having been canonicalized (in
+ // GetCookieDomainKeyWithString->CanonicalizeHost).
bool IsEquivalent(const CanonicalCookie& ecc) const {
// It seems like it would make sense to take secure and httponly into
// account, but the RFC doesn't specify this.
- return name_ == ecc.Name() && path_ == ecc.Path();
+ // NOTE: Keep this logic in-sync with TrimDuplicateCookiesForHost().
+ return (name_ == ecc.Name() && domain_ == ecc.Domain()
+ && path_ == ecc.Path());
}
void SetLastAccessDate(const base::Time& date) {
@@ -355,6 +415,7 @@ class CookieMonster::CanonicalCookie {
private:
std::string name_;
std::string value_;
+ std::string domain_;
std::string path_;
base::Time creation_date_;
base::Time last_access_date_;
@@ -364,6 +425,112 @@ class CookieMonster::CanonicalCookie {
bool httponly_;
};
+class CookieMonster::Delegate
+ : public base::RefCountedThreadSafe<CookieMonster::Delegate> {
+ public:
+ // Will be called when a cookie is added or removed. The function is passed
+ // the respective |cookie| which was added to or removed from the cookies.
+ // If |removed| is true, the cookie was deleted.
+ virtual void OnCookieChanged(const CookieMonster::CanonicalCookie& cookie,
+ bool removed) = 0;
+ protected:
+ friend class base::RefCountedThreadSafe<CookieMonster::Delegate>;
+ virtual ~Delegate() {}
+};
+
+class CookieMonster::ParsedCookie {
+ public:
+ typedef std::pair<std::string, std::string> TokenValuePair;
+ typedef std::vector<TokenValuePair> PairList;
+
+ // The maximum length of a cookie string we will try to parse
+ static const size_t kMaxCookieSize = 4096;
+ // The maximum number of Token/Value pairs. Shouldn't have more than 8.
+ static const int kMaxPairs = 16;
+
+ // Construct from a cookie string like "BLAH=1; path=/; domain=.google.com"
+ ParsedCookie(const std::string& cookie_line);
+ ~ParsedCookie() { }
+
+ // You should not call any other methods on the class if !IsValid
+ bool IsValid() const { return is_valid_; }
+
+ const std::string& Name() const { return pairs_[0].first; }
+ const std::string& Token() const { return Name(); }
+ const std::string& Value() const { return pairs_[0].second; }
+
+ bool HasPath() const { return path_index_ != 0; }
+ const std::string& Path() const { return pairs_[path_index_].second; }
+ bool HasDomain() const { return domain_index_ != 0; }
+ const std::string& Domain() const { return pairs_[domain_index_].second; }
+ bool HasExpires() const { return expires_index_ != 0; }
+ const std::string& Expires() const { return pairs_[expires_index_].second; }
+ bool HasMaxAge() const { return maxage_index_ != 0; }
+ const std::string& MaxAge() const { return pairs_[maxage_index_].second; }
+ bool IsSecure() const { return secure_index_ != 0; }
+ bool IsHttpOnly() const { return httponly_index_ != 0; }
+
+ // Returns the number of attributes, for example, returning 2 for:
+ // "BLAH=hah; path=/; domain=.google.com"
+ size_t NumberOfAttributes() const { return pairs_.size() - 1; }
+
+ // For debugging only!
+ std::string DebugString() const;
+
+ // Returns an iterator pointing to the first terminator character found in
+ // the given string.
+ static std::string::const_iterator FindFirstTerminator(const std::string& s);
+
+ // Given iterators pointing to the beginning and end of a string segment,
+ // returns as output arguments token_start and token_end to the start and end
+ // positions of a cookie attribute token name parsed from the segment, and
+ // updates the segment iterator to point to the next segment to be parsed.
+ // If no token is found, the function returns false.
+ static bool ParseToken(std::string::const_iterator* it,
+ const std::string::const_iterator& end,
+ std::string::const_iterator* token_start,
+ std::string::const_iterator* token_end);
+
+ // Given iterators pointing to the beginning and end of a string segment,
+ // returns as output arguments value_start and value_end to the start and end
+ // positions of a cookie attribute value parsed from the segment, and updates
+ // the segment iterator to point to the next segment to be parsed.
+ static void ParseValue(std::string::const_iterator* it,
+ const std::string::const_iterator& end,
+ std::string::const_iterator* value_start,
+ std::string::const_iterator* value_end);
+
+ // Same as the above functions, except the input is assumed to contain the
+ // desired token/value and nothing else.
+ static std::string ParseTokenString(const std::string& token);
+ static std::string ParseValueString(const std::string& value);
+
+ private:
+ static const char kTerminator[];
+ static const int kTerminatorLen;
+ static const char kWhitespace[];
+ static const char kValueSeparator[];
+ static const char kTokenSeparator[];
+
+ void ParseTokenValuePairs(const std::string& cookie_line);
+ void SetupAttributes();
+
+ PairList pairs_;
+ bool is_valid_;
+ // These will default to 0, but that should never be valid since the
+ // 0th index is the user supplied token/value, not an attribute.
+ // We're really never going to have more than like 8 attributes, so we
+ // could fit these into 3 bits each if we're worried about size...
+ size_t path_index_;
+ size_t domain_index_;
+ size_t expires_index_;
+ size_t maxage_index_;
+ size_t secure_index_;
+ size_t httponly_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(ParsedCookie);
+};
+
typedef base::RefCountedThreadSafe<CookieMonster::PersistentCookieStore>
RefcountedPersistentCookieStore;