| // Copyright (c) 2012 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 "net/url_request/url_request_throttler_manager.h" |
| |
| #include "base/logging.h" |
| #include "base/strings/string_util.h" |
| #include "net/base/url_util.h" |
| #include "net/log/net_log.h" |
| #include "net/log/net_log_event_type.h" |
| #include "net/log/net_log_source_type.h" |
| |
| namespace net { |
| |
| const unsigned int URLRequestThrottlerManager::kMaximumNumberOfEntries = 1500; |
| const unsigned int URLRequestThrottlerManager::kRequestsBetweenCollecting = 200; |
| |
| URLRequestThrottlerManager::URLRequestThrottlerManager() |
| : requests_since_last_gc_(0), |
| logged_for_localhost_disabled_(false), |
| registered_from_thread_(base::kInvalidThreadId) { |
| url_id_replacements_.ClearPassword(); |
| url_id_replacements_.ClearUsername(); |
| url_id_replacements_.ClearQuery(); |
| url_id_replacements_.ClearRef(); |
| |
| NetworkChangeNotifier::AddIPAddressObserver(this); |
| NetworkChangeNotifier::AddConnectionTypeObserver(this); |
| } |
| |
| URLRequestThrottlerManager::~URLRequestThrottlerManager() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| NetworkChangeNotifier::RemoveIPAddressObserver(this); |
| NetworkChangeNotifier::RemoveConnectionTypeObserver(this); |
| |
| // Since the manager object might conceivably go away before the |
| // entries, detach the entries' back-pointer to the manager. |
| auto i = url_entries_.begin(); |
| while (i != url_entries_.end()) { |
| if (i->second.get() != NULL) { |
| i->second->DetachManager(); |
| } |
| ++i; |
| } |
| |
| // Delete all entries. |
| url_entries_.clear(); |
| } |
| |
| scoped_refptr<URLRequestThrottlerEntryInterface> |
| URLRequestThrottlerManager::RegisterRequestUrl(const GURL &url) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| // Normalize the url. |
| std::string url_id = GetIdFromUrl(url); |
| |
| // Periodically garbage collect old entries. |
| GarbageCollectEntriesIfNecessary(); |
| |
| // Find the entry in the map or create a new NULL entry. |
| scoped_refptr<URLRequestThrottlerEntry>& entry = url_entries_[url_id]; |
| |
| // If the entry exists but could be garbage collected at this point, we |
| // start with a fresh entry so that we possibly back off a bit less |
| // aggressively (i.e. this resets the error count when the entry's URL |
| // hasn't been requested in long enough). |
| if (entry.get() && entry->IsEntryOutdated()) { |
| entry = NULL; |
| } |
| |
| // Create the entry if needed. |
| if (entry.get() == NULL) { |
| entry = new URLRequestThrottlerEntry(this, url_id); |
| |
| // We only disable back-off throttling on an entry that we have |
| // just constructed. This is to allow unit tests to explicitly override |
| // the entry for localhost URLs. |
| if (IsLocalhost(url)) { |
| if (!logged_for_localhost_disabled_ && IsLocalhost(url)) { |
| std::string host = url.host(); |
| logged_for_localhost_disabled_ = true; |
| net_log_.AddEvent(NetLogEventType::THROTTLING_DISABLED_FOR_HOST, |
| NetLog::StringCallback("host", &host)); |
| } |
| |
| // TODO(joi): Once sliding window is separate from back-off throttling, |
| // we can simply return a dummy implementation of |
| // URLRequestThrottlerEntryInterface here that never blocks anything. |
| entry->DisableBackoffThrottling(); |
| } |
| } |
| |
| return entry; |
| } |
| |
| void URLRequestThrottlerManager::OverrideEntryForTests( |
| const GURL& url, |
| URLRequestThrottlerEntry* entry) { |
| // Normalize the url. |
| std::string url_id = GetIdFromUrl(url); |
| |
| // Periodically garbage collect old entries. |
| GarbageCollectEntriesIfNecessary(); |
| |
| url_entries_[url_id] = entry; |
| } |
| |
| void URLRequestThrottlerManager::EraseEntryForTests(const GURL& url) { |
| // Normalize the url. |
| std::string url_id = GetIdFromUrl(url); |
| url_entries_.erase(url_id); |
| } |
| |
| void URLRequestThrottlerManager::set_net_log(NetLog* net_log) { |
| DCHECK(net_log); |
| net_log_ = NetLogWithSource::Make( |
| net_log, NetLogSourceType::EXPONENTIAL_BACKOFF_THROTTLING); |
| } |
| |
| NetLog* URLRequestThrottlerManager::net_log() const { |
| return net_log_.net_log(); |
| } |
| |
| void URLRequestThrottlerManager::OnIPAddressChanged() { |
| OnNetworkChange(); |
| } |
| |
| void URLRequestThrottlerManager::OnConnectionTypeChanged( |
| NetworkChangeNotifier::ConnectionType type) { |
| OnNetworkChange(); |
| } |
| |
| std::string URLRequestThrottlerManager::GetIdFromUrl(const GURL& url) const { |
| if (!url.is_valid()) |
| return url.possibly_invalid_spec(); |
| |
| GURL id = url.ReplaceComponents(url_id_replacements_); |
| return base::ToLowerASCII(id.spec()); |
| } |
| |
| void URLRequestThrottlerManager::GarbageCollectEntriesIfNecessary() { |
| requests_since_last_gc_++; |
| if (requests_since_last_gc_ < kRequestsBetweenCollecting) |
| return; |
| requests_since_last_gc_ = 0; |
| |
| GarbageCollectEntries(); |
| } |
| |
| void URLRequestThrottlerManager::GarbageCollectEntries() { |
| auto i = url_entries_.begin(); |
| while (i != url_entries_.end()) { |
| if ((i->second)->IsEntryOutdated()) { |
| url_entries_.erase(i++); |
| } else { |
| ++i; |
| } |
| } |
| |
| // In case something broke we want to make sure not to grow indefinitely. |
| while (url_entries_.size() > kMaximumNumberOfEntries) { |
| url_entries_.erase(url_entries_.begin()); |
| } |
| } |
| |
| void URLRequestThrottlerManager::OnNetworkChange() { |
| // Remove all entries. Any entries that in-flight requests have a reference |
| // to will live until those requests end, and these entries may be |
| // inconsistent with new entries for the same URLs, but since what we |
| // want is a clean slate for the new connection type, this is OK. |
| url_entries_.clear(); |
| requests_since_last_gc_ = 0; |
| } |
| |
| } // namespace net |