// 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. #ifndef CHROME_BROWSER_NET_EVICTED_DOMAIN_COOKIE_COUNTER_H_ #define CHROME_BROWSER_NET_EVICTED_DOMAIN_COOKIE_COUNTER_H_ #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 { public: // 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), expiry_time(expiry_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 { public: 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. EvictedDomainCookieCounter( 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; private: // 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_; DISALLOW_COPY_AND_ASSIGN(EvictedDomainCookieCounter); }; } // namespace chrome_browser_net #endif // CHROME_BROWSER_NET_EVICTED_DOMAIN_COOKIE_COUNTER_H_