| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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 #include "net/url_request/url_request_throttler_manager.h" | |
| 6 | |
| 7 #include "base/logging.h" | |
| 8 #include "base/metrics/field_trial.h" | |
| 9 #include "base/metrics/histogram.h" | |
| 10 #include "base/strings/string_util.h" | |
| 11 #include "net/base/net_log.h" | |
| 12 #include "net/base/net_util.h" | |
| 13 | |
| 14 namespace net { | |
| 15 | |
| 16 const unsigned int URLRequestThrottlerManager::kMaximumNumberOfEntries = 1500; | |
| 17 const unsigned int URLRequestThrottlerManager::kRequestsBetweenCollecting = 200; | |
| 18 | |
| 19 URLRequestThrottlerManager::URLRequestThrottlerManager() | |
| 20 : requests_since_last_gc_(0), | |
| 21 enable_thread_checks_(false), | |
| 22 logged_for_localhost_disabled_(false), | |
| 23 registered_from_thread_(base::kInvalidThreadId) { | |
| 24 url_id_replacements_.ClearPassword(); | |
| 25 url_id_replacements_.ClearUsername(); | |
| 26 url_id_replacements_.ClearQuery(); | |
| 27 url_id_replacements_.ClearRef(); | |
| 28 | |
| 29 NetworkChangeNotifier::AddIPAddressObserver(this); | |
| 30 NetworkChangeNotifier::AddConnectionTypeObserver(this); | |
| 31 } | |
| 32 | |
| 33 URLRequestThrottlerManager::~URLRequestThrottlerManager() { | |
| 34 NetworkChangeNotifier::RemoveIPAddressObserver(this); | |
| 35 NetworkChangeNotifier::RemoveConnectionTypeObserver(this); | |
| 36 | |
| 37 // Since the manager object might conceivably go away before the | |
| 38 // entries, detach the entries' back-pointer to the manager. | |
| 39 UrlEntryMap::iterator i = url_entries_.begin(); | |
| 40 while (i != url_entries_.end()) { | |
| 41 if (i->second.get() != NULL) { | |
| 42 i->second->DetachManager(); | |
| 43 } | |
| 44 ++i; | |
| 45 } | |
| 46 | |
| 47 // Delete all entries. | |
| 48 url_entries_.clear(); | |
| 49 } | |
| 50 | |
| 51 scoped_refptr<URLRequestThrottlerEntryInterface> | |
| 52 URLRequestThrottlerManager::RegisterRequestUrl(const GURL &url) { | |
| 53 DCHECK(!enable_thread_checks_ || CalledOnValidThread()); | |
| 54 | |
| 55 // Normalize the url. | |
| 56 std::string url_id = GetIdFromUrl(url); | |
| 57 | |
| 58 // Periodically garbage collect old entries. | |
| 59 GarbageCollectEntriesIfNecessary(); | |
| 60 | |
| 61 // Find the entry in the map or create a new NULL entry. | |
| 62 scoped_refptr<URLRequestThrottlerEntry>& entry = url_entries_[url_id]; | |
| 63 | |
| 64 // If the entry exists but could be garbage collected at this point, we | |
| 65 // start with a fresh entry so that we possibly back off a bit less | |
| 66 // aggressively (i.e. this resets the error count when the entry's URL | |
| 67 // hasn't been requested in long enough). | |
| 68 if (entry.get() && entry->IsEntryOutdated()) { | |
| 69 entry = NULL; | |
| 70 } | |
| 71 | |
| 72 // Create the entry if needed. | |
| 73 if (entry.get() == NULL) { | |
| 74 entry = new URLRequestThrottlerEntry(this, url_id); | |
| 75 | |
| 76 // We only disable back-off throttling on an entry that we have | |
| 77 // just constructed. This is to allow unit tests to explicitly override | |
| 78 // the entry for localhost URLs. Given that we do not attempt to | |
| 79 // disable throttling for entries already handed out (see comment | |
| 80 // in AddToOptOutList), this is not a problem. | |
| 81 std::string host = url.host(); | |
| 82 if (opt_out_hosts_.find(host) != opt_out_hosts_.end() || | |
| 83 IsLocalhost(host)) { | |
| 84 if (!logged_for_localhost_disabled_ && IsLocalhost(host)) { | |
| 85 logged_for_localhost_disabled_ = true; | |
| 86 net_log_.AddEvent(NetLog::TYPE_THROTTLING_DISABLED_FOR_HOST, | |
| 87 NetLog::StringCallback("host", &host)); | |
| 88 } | |
| 89 | |
| 90 // TODO(joi): Once sliding window is separate from back-off throttling, | |
| 91 // we can simply return a dummy implementation of | |
| 92 // URLRequestThrottlerEntryInterface here that never blocks anything (and | |
| 93 // not keep entries in url_entries_ for opted-out sites). | |
| 94 entry->DisableBackoffThrottling(); | |
| 95 } | |
| 96 } | |
| 97 | |
| 98 return entry; | |
| 99 } | |
| 100 | |
| 101 void URLRequestThrottlerManager::AddToOptOutList(const std::string& host) { | |
| 102 // There is an edge case here that we are not handling, to keep things | |
| 103 // simple. If a host starts adding the opt-out header to its responses | |
| 104 // after there are already one or more entries in url_entries_ for that | |
| 105 // host, the pre-existing entries may still perform back-off throttling. | |
| 106 // In practice, this would almost never occur. | |
| 107 if (opt_out_hosts_.find(host) == opt_out_hosts_.end()) { | |
| 108 UMA_HISTOGRAM_COUNTS("Throttling.SiteOptedOut", 1); | |
| 109 | |
| 110 net_log_.EndEvent(NetLog::TYPE_THROTTLING_DISABLED_FOR_HOST, | |
| 111 NetLog::StringCallback("host", &host)); | |
| 112 opt_out_hosts_.insert(host); | |
| 113 } | |
| 114 } | |
| 115 | |
| 116 void URLRequestThrottlerManager::OverrideEntryForTests( | |
| 117 const GURL& url, | |
| 118 URLRequestThrottlerEntry* entry) { | |
| 119 // Normalize the url. | |
| 120 std::string url_id = GetIdFromUrl(url); | |
| 121 | |
| 122 // Periodically garbage collect old entries. | |
| 123 GarbageCollectEntriesIfNecessary(); | |
| 124 | |
| 125 url_entries_[url_id] = entry; | |
| 126 } | |
| 127 | |
| 128 void URLRequestThrottlerManager::EraseEntryForTests(const GURL& url) { | |
| 129 // Normalize the url. | |
| 130 std::string url_id = GetIdFromUrl(url); | |
| 131 url_entries_.erase(url_id); | |
| 132 } | |
| 133 | |
| 134 void URLRequestThrottlerManager::set_enable_thread_checks(bool enable) { | |
| 135 enable_thread_checks_ = enable; | |
| 136 } | |
| 137 | |
| 138 bool URLRequestThrottlerManager::enable_thread_checks() const { | |
| 139 return enable_thread_checks_; | |
| 140 } | |
| 141 | |
| 142 void URLRequestThrottlerManager::set_net_log(NetLog* net_log) { | |
| 143 DCHECK(net_log); | |
| 144 net_log_ = BoundNetLog::Make(net_log, | |
| 145 NetLog::SOURCE_EXPONENTIAL_BACKOFF_THROTTLING); | |
| 146 } | |
| 147 | |
| 148 NetLog* URLRequestThrottlerManager::net_log() const { | |
| 149 return net_log_.net_log(); | |
| 150 } | |
| 151 | |
| 152 void URLRequestThrottlerManager::OnIPAddressChanged() { | |
| 153 OnNetworkChange(); | |
| 154 } | |
| 155 | |
| 156 void URLRequestThrottlerManager::OnConnectionTypeChanged( | |
| 157 NetworkChangeNotifier::ConnectionType type) { | |
| 158 OnNetworkChange(); | |
| 159 } | |
| 160 | |
| 161 std::string URLRequestThrottlerManager::GetIdFromUrl(const GURL& url) const { | |
| 162 if (!url.is_valid()) | |
| 163 return url.possibly_invalid_spec(); | |
| 164 | |
| 165 GURL id = url.ReplaceComponents(url_id_replacements_); | |
| 166 return base::StringToLowerASCII(id.spec()).c_str(); | |
| 167 } | |
| 168 | |
| 169 void URLRequestThrottlerManager::GarbageCollectEntriesIfNecessary() { | |
| 170 requests_since_last_gc_++; | |
| 171 if (requests_since_last_gc_ < kRequestsBetweenCollecting) | |
| 172 return; | |
| 173 requests_since_last_gc_ = 0; | |
| 174 | |
| 175 GarbageCollectEntries(); | |
| 176 } | |
| 177 | |
| 178 void URLRequestThrottlerManager::GarbageCollectEntries() { | |
| 179 UrlEntryMap::iterator i = url_entries_.begin(); | |
| 180 while (i != url_entries_.end()) { | |
| 181 if ((i->second)->IsEntryOutdated()) { | |
| 182 url_entries_.erase(i++); | |
| 183 } else { | |
| 184 ++i; | |
| 185 } | |
| 186 } | |
| 187 | |
| 188 // In case something broke we want to make sure not to grow indefinitely. | |
| 189 while (url_entries_.size() > kMaximumNumberOfEntries) { | |
| 190 url_entries_.erase(url_entries_.begin()); | |
| 191 } | |
| 192 } | |
| 193 | |
| 194 void URLRequestThrottlerManager::OnNetworkChange() { | |
| 195 // Remove all entries. Any entries that in-flight requests have a reference | |
| 196 // to will live until those requests end, and these entries may be | |
| 197 // inconsistent with new entries for the same URLs, but since what we | |
| 198 // want is a clean slate for the new connection type, this is OK. | |
| 199 url_entries_.clear(); | |
| 200 requests_since_last_gc_ = 0; | |
| 201 } | |
| 202 | |
| 203 } // namespace net | |
| OLD | NEW |