1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef CHROME_BROWSER_NET_EVICTED_DOMAIN_COOKIE_COUNTER_H_
6#define CHROME_BROWSER_NET_EVICTED_DOMAIN_COOKIE_COUNTER_H_
7
8#include <map>
9#include <string>
10
11#include "base/basictypes.h"
12#include "base/compiler_specific.h"
13#include "base/memory/ref_counted.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/time/time.h"
16#include "net/cookies/cookie_monster.h"
17
18namespace net {
19class CanonicalCookie;
20}  // namespace net
21
22namespace chrome_browser_net {
23
24// The Evicted Domain Cookie Counter generates statistics on "wrongly evicted"
25// cookies, i.e., cookies that were "evicted" (on reaching domain cookie limit)
26// but are then "reinstated" later because they were important. A specific
27// scenario is as follows: a long-lived login session cookie gets evicted owing
28// to its age, thereby forcing the user to lose session, and is reinstated when
29// the user re-authenticates.
30//
31// A solution to the above problem is the Cookie Priority Field, which enables
32// servers to protect important cookies, thereby decreasing the chances that
33// these cookies are wrongly evicted. To measure the effectiveness of this
34// solution, we will compare eviction user metrics before vs. after the fix.
35//
36// Specifically, we wish to record user metrics on "reinstatement delay", i.e.,
37// the duration between eviction and reinstatement of cookie. We expect that
38// after the fix, average reinstatement delays will increase, since low priority
39// cookies are less likely to be reinstated after eviction.
40//
41// Metrics for Google domains are tracked separately.
42//
43class EvictedDomainCookieCounter : public net::CookieMonster::Delegate {
44 public:
45  // Structure to store sanitized data from CanonicalCookie.
46  struct EvictedCookie {
47    EvictedCookie(base::Time eviction_time_in,
48                  base::Time expiry_time_in,
49                  bool is_google_in)
50        : eviction_time(eviction_time_in),
51          expiry_time(expiry_time_in),
52          is_google(is_google_in) {}
53
54    bool is_expired(const base::Time& current_time) const {
55      return !expiry_time.is_null() && current_time >= expiry_time;
56    }
57
58    base::Time eviction_time;
59    base::Time expiry_time;
60    bool is_google;
61  };
62
63  class Delegate {
64   public:
65    virtual ~Delegate() {}
66
67    // Called when a stored evicted cookie is reinstated.
68    virtual void Report(const EvictedCookie& evicted_cookie,
69                        const base::Time& reinstatement_time) = 0;
70
71    // Getter of time is placed here to enable mocks.
72    virtual base::Time CurrentTime() const = 0;
73  };
74
75  // |next_cookie_monster_delegate| can be NULL.
76  explicit EvictedDomainCookieCounter(
77      scoped_refptr<net::CookieMonster::Delegate> next_cookie_monster_delegate);
78
79  // Constructor exposed for testing only.
80  EvictedDomainCookieCounter(
81      scoped_refptr<net::CookieMonster::Delegate> next_cookie_monster_delegate,
82      scoped_ptr<Delegate> cookie_counter_delegate,
83      size_t max_size,
84      size_t purge_count);
85
86  // Returns the number of evicted cookies stored.
87  size_t GetStorageSize() const;
88
89  // CookieMonster::Delegate implementation.
90  virtual void OnCookieChanged(const net::CanonicalCookie& cookie,
91                               bool removed,
92                               ChangeCause cause) OVERRIDE;
93  virtual void OnLoaded() OVERRIDE;
94
95 private:
96  // Identifier of an evicted cookie.
97  typedef std::string EvictedCookieKey;
98
99  // Storage class of evicted cookie.
100  typedef std::map<EvictedCookieKey, EvictedCookie*> EvictedCookieMap;
101
102  virtual ~EvictedDomainCookieCounter();
103
104  // Computes key for |cookie| compatible with CanonicalCookie::IsEquivalent(),
105  // i.e., IsEquivalent(a, b) ==> GetKey(a) == GetKey(b).
106  static EvictedCookieKey GetKey(const net::CanonicalCookie& cookie);
107
108  // Comparator for sorting, to make recently evicted cookies appear earlier.
109  static bool CompareEvictedCookie(
110      const EvictedCookieMap::iterator evicted_cookie1,
111      const EvictedCookieMap::iterator evicted_cookie2);
112
113  // If too many evicted cookies are stored, delete the expired ones, then
114  // delete cookies that were evicted the longest, until size limit reached.
115  void GarbageCollect(const base::Time& current_time);
116
117  // Called when a cookie is evicted. Adds the evicted cookie to storage,
118  // possibly replacing an existing equivalent cookie.
119  void StoreEvictedCookie(const EvictedCookieKey& key,
120                          const net::CanonicalCookie& cookie,
121                          const base::Time& current_time);
122
123  // Called when a new cookie is added. If reinstatement occurs, then notifies
124  // |cookie_counter_delegate_| and then removes the evicted cookie.
125  void ProcessNewCookie(const EvictedCookieKey& key,
126                        const net::CanonicalCookie& cookie,
127                        const base::Time& current_time);
128
129  // Another delegate to forward events to.
130  scoped_refptr<net::CookieMonster::Delegate> next_cookie_monster_delegate_;
131
132  scoped_ptr<Delegate> cookie_counter_delegate_;
133
134  EvictedCookieMap evicted_cookies_;
135
136  // Capacity of the evicted cookie storage, before garbage collection occurs.
137  const size_t max_size_;
138
139  // After garbage collection, size reduces to <= |max_size_| - |purge_count_|.
140  const size_t purge_count_;
141
142  DISALLOW_COPY_AND_ASSIGN(EvictedDomainCookieCounter);
143};
144
145}  // namespace chrome_browser_net
146
147#endif  // CHROME_BROWSER_NET_EVICTED_DOMAIN_COOKIE_COUNTER_H_
148