• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/url_request/url_request_throttler_manager.h"
6 
7 #include "base/check_op.h"
8 #include "base/strings/string_util.h"
9 #include "net/base/url_util.h"
10 #include "net/log/net_log.h"
11 #include "net/log/net_log_event_type.h"
12 #include "net/log/net_log_source_type.h"
13 
14 namespace net {
15 
16 const unsigned int URLRequestThrottlerManager::kMaximumNumberOfEntries = 1500;
17 const unsigned int URLRequestThrottlerManager::kRequestsBetweenCollecting = 200;
18 
URLRequestThrottlerManager()19 URLRequestThrottlerManager::URLRequestThrottlerManager() {
20   url_id_replacements_.ClearPassword();
21   url_id_replacements_.ClearUsername();
22   url_id_replacements_.ClearQuery();
23   url_id_replacements_.ClearRef();
24 
25   NetworkChangeNotifier::AddIPAddressObserver(this);
26   NetworkChangeNotifier::AddConnectionTypeObserver(this);
27 }
28 
~URLRequestThrottlerManager()29 URLRequestThrottlerManager::~URLRequestThrottlerManager() {
30   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
31   NetworkChangeNotifier::RemoveIPAddressObserver(this);
32   NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
33 
34   // Since the manager object might conceivably go away before the
35   // entries, detach the entries' back-pointer to the manager.
36   auto i = url_entries_.begin();
37   while (i != url_entries_.end()) {
38     if (i->second.get() != nullptr) {
39       i->second->DetachManager();
40     }
41     ++i;
42   }
43 
44   // Delete all entries.
45   url_entries_.clear();
46 }
47 
48 scoped_refptr<URLRequestThrottlerEntryInterface>
RegisterRequestUrl(const GURL & url)49     URLRequestThrottlerManager::RegisterRequestUrl(const GURL &url) {
50   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
51 
52   // Normalize the url.
53   std::string url_id = GetIdFromUrl(url);
54 
55   // Periodically garbage collect old entries.
56   GarbageCollectEntriesIfNecessary();
57 
58   // Find the entry in the map or create a new NULL entry.
59   scoped_refptr<URLRequestThrottlerEntry>& entry = url_entries_[url_id];
60 
61   // If the entry exists but could be garbage collected at this point, we
62   // start with a fresh entry so that we possibly back off a bit less
63   // aggressively (i.e. this resets the error count when the entry's URL
64   // hasn't been requested in long enough).
65   if (entry.get() && entry->IsEntryOutdated()) {
66     entry = nullptr;
67   }
68 
69   // Create the entry if needed.
70   if (entry.get() == nullptr) {
71     entry = base::MakeRefCounted<URLRequestThrottlerEntry>(this, url_id);
72 
73     // We only disable back-off throttling on an entry that we have
74     // just constructed.  This is to allow unit tests to explicitly override
75     // the entry for localhost URLs.
76     if (IsLocalhost(url)) {
77       if (!logged_for_localhost_disabled_ && IsLocalhost(url)) {
78         logged_for_localhost_disabled_ = true;
79         net_log_.AddEventWithStringParams(
80             NetLogEventType::THROTTLING_DISABLED_FOR_HOST, "host", url.host());
81       }
82 
83       // TODO(joi): Once sliding window is separate from back-off throttling,
84       // we can simply return a dummy implementation of
85       // URLRequestThrottlerEntryInterface here that never blocks anything.
86       entry->DisableBackoffThrottling();
87     }
88   }
89 
90   return entry;
91 }
92 
OverrideEntryForTests(const GURL & url,scoped_refptr<URLRequestThrottlerEntry> entry)93 void URLRequestThrottlerManager::OverrideEntryForTests(
94     const GURL& url,
95     scoped_refptr<URLRequestThrottlerEntry> entry) {
96   // Normalize the url.
97   std::string url_id = GetIdFromUrl(url);
98 
99   // Periodically garbage collect old entries.
100   GarbageCollectEntriesIfNecessary();
101 
102   url_entries_[url_id] = std::move(entry);
103 }
104 
EraseEntryForTests(const GURL & url)105 void URLRequestThrottlerManager::EraseEntryForTests(const GURL& url) {
106   // Normalize the url.
107   std::string url_id = GetIdFromUrl(url);
108   url_entries_.erase(url_id);
109 }
110 
set_net_log(NetLog * net_log)111 void URLRequestThrottlerManager::set_net_log(NetLog* net_log) {
112   DCHECK(net_log);
113   net_log_ = NetLogWithSource::Make(
114       net_log, NetLogSourceType::EXPONENTIAL_BACKOFF_THROTTLING);
115 }
116 
net_log() const117 NetLog* URLRequestThrottlerManager::net_log() const {
118   return net_log_.net_log();
119 }
120 
OnIPAddressChanged()121 void URLRequestThrottlerManager::OnIPAddressChanged() {
122   OnNetworkChange();
123 }
124 
OnConnectionTypeChanged(NetworkChangeNotifier::ConnectionType type)125 void URLRequestThrottlerManager::OnConnectionTypeChanged(
126     NetworkChangeNotifier::ConnectionType type) {
127   OnNetworkChange();
128 }
129 
GetIdFromUrl(const GURL & url) const130 std::string URLRequestThrottlerManager::GetIdFromUrl(const GURL& url) const {
131   if (!url.is_valid())
132     return url.possibly_invalid_spec();
133 
134   GURL id = url.ReplaceComponents(url_id_replacements_);
135   return base::ToLowerASCII(id.spec());
136 }
137 
GarbageCollectEntriesIfNecessary()138 void URLRequestThrottlerManager::GarbageCollectEntriesIfNecessary() {
139   requests_since_last_gc_++;
140   if (requests_since_last_gc_ < kRequestsBetweenCollecting)
141     return;
142   requests_since_last_gc_ = 0;
143 
144   GarbageCollectEntries();
145 }
146 
GarbageCollectEntries()147 void URLRequestThrottlerManager::GarbageCollectEntries() {
148   auto i = url_entries_.begin();
149   while (i != url_entries_.end()) {
150     if ((i->second)->IsEntryOutdated()) {
151       url_entries_.erase(i++);
152     } else {
153       ++i;
154     }
155   }
156 
157   // In case something broke we want to make sure not to grow indefinitely.
158   while (url_entries_.size() > kMaximumNumberOfEntries) {
159     url_entries_.erase(url_entries_.begin());
160   }
161 }
162 
OnNetworkChange()163 void URLRequestThrottlerManager::OnNetworkChange() {
164   // Remove all entries.  Any entries that in-flight requests have a reference
165   // to will live until those requests end, and these entries may be
166   // inconsistent with new entries for the same URLs, but since what we
167   // want is a clean slate for the new connection type, this is OK.
168   url_entries_.clear();
169   requests_since_last_gc_ = 0;
170 }
171 
172 }  // namespace net
173