blob: 5731fa91ade44a8a8667b574f55b8d6e7183386a [file] [log] [blame]
// Copyright 2013 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.
#include <map>
#include <string>
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
#include "net/cookies/cookie_monster.h"
namespace net {
class CanonicalCookie;
} // namespace net
namespace chrome_browser_net {
// The Evicted Domain Cookie Counter generates statistics on "wrongly evicted"
// cookies, i.e., cookies that were "evicted" (on reaching domain cookie limit)
// but are then "reinstated" later because they were important. A specific
// scenario is as follows: a long-lived login session cookie gets evicted owing
// to its age, thereby forcing the user to lose session, and is reinstated when
// the user re-authenticates.
// A solution to the above problem is the Cookie Priority Field, which enables
// servers to protect important cookies, thereby decreasing the chances that
// these cookies are wrongly evicted. To measure the effectiveness of this
// solution, we will compare eviction user metrics before vs. after the fix.
// Specifically, we wish to record user metrics on "reinstatement delay", i.e.,
// the duration between eviction and reinstatement of cookie. We expect that
// after the fix, average reinstatement delays will increase, since low priority
// cookies are less likely to be reinstated after eviction.
// Metrics for Google domains are tracked separately.
class EvictedDomainCookieCounter : public net::CookieMonster::Delegate {
// Structure to store sanitized data from CanonicalCookie.
struct EvictedCookie {
EvictedCookie(base::Time eviction_time_in,
base::Time expiry_time_in,
bool is_google_in)
: eviction_time(eviction_time_in),
is_google(is_google_in) {}
bool is_expired(const base::Time& current_time) const {
return !expiry_time.is_null() && current_time >= expiry_time;
base::Time eviction_time;
base::Time expiry_time;
bool is_google;
class Delegate {
virtual ~Delegate() {}
// Called when a stored evicted cookie is reinstated.
virtual void Report(const EvictedCookie& evicted_cookie,
const base::Time& reinstatement_time) = 0;
// Getter of time is placed here to enable mocks.
virtual base::Time CurrentTime() const = 0;
// |next_cookie_monster_delegate| can be NULL.
explicit EvictedDomainCookieCounter(
scoped_refptr<net::CookieMonster::Delegate> next_cookie_monster_delegate);
// Constructor exposed for testing only.
scoped_refptr<net::CookieMonster::Delegate> next_cookie_monster_delegate,
scoped_ptr<Delegate> cookie_counter_delegate,
size_t max_size,
size_t purge_count);
// Returns the number of evicted cookies stored.
size_t GetStorageSize() const;
// CookieMonster::Delegate implementation.
virtual void OnCookieChanged(const net::CanonicalCookie& cookie,
bool removed,
ChangeCause cause) OVERRIDE;
virtual void OnLoaded() OVERRIDE;
// Identifier of an evicted cookie.
typedef std::string EvictedCookieKey;
// Storage class of evicted cookie.
typedef std::map<EvictedCookieKey, EvictedCookie*> EvictedCookieMap;
virtual ~EvictedDomainCookieCounter();
// Computes key for |cookie| compatible with CanonicalCookie::IsEquivalent(),
// i.e., IsEquivalent(a, b) ==> GetKey(a) == GetKey(b).
static EvictedCookieKey GetKey(const net::CanonicalCookie& cookie);
// Comparator for sorting, to make recently evicted cookies appear earlier.
static bool CompareEvictedCookie(
const EvictedCookieMap::iterator evicted_cookie1,
const EvictedCookieMap::iterator evicted_cookie2);
// If too many evicted cookies are stored, delete the expired ones, then
// delete cookies that were evicted the longest, until size limit reached.
void GarbageCollect(const base::Time& current_time);
// Called when a cookie is evicted. Adds the evicted cookie to storage,
// possibly replacing an existing equivalent cookie.
void StoreEvictedCookie(const EvictedCookieKey& key,
const net::CanonicalCookie& cookie,
const base::Time& current_time);
// Called when a new cookie is added. If reinstatement occurs, then notifies
// |cookie_counter_delegate_| and then removes the evicted cookie.
void ProcessNewCookie(const EvictedCookieKey& key,
const net::CanonicalCookie& cookie,
const base::Time& current_time);
// Another delegate to forward events to.
scoped_refptr<net::CookieMonster::Delegate> next_cookie_monster_delegate_;
scoped_ptr<Delegate> cookie_counter_delegate_;
EvictedCookieMap evicted_cookies_;
// Capacity of the evicted cookie storage, before garbage collection occurs.
const size_t max_size_;
// After garbage collection, size reduces to <= |max_size_| - |purge_count_|.
const size_t purge_count_;
} // namespace chrome_browser_net