1 // Copyright (c) 2011 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/string_util.h" 9 #include "net/base/net_util.h" 10 11 namespace net { 12 13 const unsigned int URLRequestThrottlerManager::kMaximumNumberOfEntries = 1500; 14 const unsigned int URLRequestThrottlerManager::kRequestsBetweenCollecting = 200; 15 16 URLRequestThrottlerManager* URLRequestThrottlerManager::GetInstance() { 17 return Singleton<URLRequestThrottlerManager>::get(); 18 } 19 20 scoped_refptr<URLRequestThrottlerEntryInterface> 21 URLRequestThrottlerManager::RegisterRequestUrl(const GURL &url) { 22 DCHECK(!enable_thread_checks_ || CalledOnValidThread()); 23 24 // Normalize the url. 25 std::string url_id = GetIdFromUrl(url); 26 27 // Periodically garbage collect old entries. 28 GarbageCollectEntriesIfNecessary(); 29 30 // Find the entry in the map or create it. 31 scoped_refptr<URLRequestThrottlerEntry>& entry = url_entries_[url_id]; 32 if (entry.get() == NULL) { 33 entry = new URLRequestThrottlerEntry(this); 34 35 // We only disable back-off throttling on an entry that we have 36 // just constructed. This is to allow unit tests to explicitly override 37 // the entry for localhost URLs. Given that we do not attempt to 38 // disable throttling for entries already handed out (see comment 39 // in AddToOptOutList), this is not a problem. 40 std::string host = url.host(); 41 if (opt_out_hosts_.find(host) != opt_out_hosts_.end() || 42 IsLocalhost(host)) { 43 // TODO(joi): Once sliding window is separate from back-off throttling, 44 // we can simply return a dummy implementation of 45 // URLRequestThrottlerEntryInterface here that never blocks anything (and 46 // not keep entries in url_entries_ for opted-out sites). 47 entry->DisableBackoffThrottling(); 48 } 49 } 50 51 return entry; 52 } 53 54 void URLRequestThrottlerManager::AddToOptOutList(const std::string& host) { 55 // There is an edge case here that we are not handling, to keep things 56 // simple. If a host starts adding the opt-out header to its responses 57 // after there are already one or more entries in url_entries_ for that 58 // host, the pre-existing entries may still perform back-off throttling. 59 // In practice, this would almost never occur. 60 opt_out_hosts_.insert(host); 61 } 62 63 void URLRequestThrottlerManager::OverrideEntryForTests( 64 const GURL& url, 65 URLRequestThrottlerEntry* entry) { 66 // Normalize the url. 67 std::string url_id = GetIdFromUrl(url); 68 69 // Periodically garbage collect old entries. 70 GarbageCollectEntriesIfNecessary(); 71 72 url_entries_[url_id] = entry; 73 } 74 75 void URLRequestThrottlerManager::EraseEntryForTests(const GURL& url) { 76 // Normalize the url. 77 std::string url_id = GetIdFromUrl(url); 78 url_entries_.erase(url_id); 79 } 80 81 void URLRequestThrottlerManager::set_enable_thread_checks(bool enable) { 82 enable_thread_checks_ = enable; 83 } 84 85 bool URLRequestThrottlerManager::enable_thread_checks() const { 86 return enable_thread_checks_; 87 } 88 89 void URLRequestThrottlerManager::set_enforce_throttling(bool enforce) { 90 enforce_throttling_ = enforce; 91 } 92 93 bool URLRequestThrottlerManager::enforce_throttling() { 94 return enforce_throttling_; 95 } 96 97 // TODO(joi): Turn throttling on by default when appropriate. 98 URLRequestThrottlerManager::URLRequestThrottlerManager() 99 : requests_since_last_gc_(0), 100 enforce_throttling_(false), 101 enable_thread_checks_(false) { 102 // Construction/destruction is on main thread (because BrowserMain 103 // retrieves an instance to call InitializeOptions), but is from then on 104 // used on I/O thread. 105 DetachFromThread(); 106 107 url_id_replacements_.ClearPassword(); 108 url_id_replacements_.ClearUsername(); 109 url_id_replacements_.ClearQuery(); 110 url_id_replacements_.ClearRef(); 111 } 112 113 URLRequestThrottlerManager::~URLRequestThrottlerManager() { 114 // Destruction is on main thread (AtExit), but real use is on I/O thread. 115 DetachFromThread(); 116 117 // Since, for now, the manager object might conceivably go away before 118 // the entries, detach the entries' back-pointer to the manager. 119 // 120 // TODO(joi): Revisit whether to make entries non-refcounted. 121 UrlEntryMap::iterator i = url_entries_.begin(); 122 while (i != url_entries_.end()) { 123 if (i->second != NULL) { 124 i->second->DetachManager(); 125 } 126 ++i; 127 } 128 129 // Delete all entries. 130 url_entries_.clear(); 131 } 132 133 std::string URLRequestThrottlerManager::GetIdFromUrl(const GURL& url) const { 134 if (!url.is_valid()) 135 return url.possibly_invalid_spec(); 136 137 GURL id = url.ReplaceComponents(url_id_replacements_); 138 return StringToLowerASCII(id.spec()).c_str(); 139 } 140 141 void URLRequestThrottlerManager::GarbageCollectEntriesIfNecessary() { 142 requests_since_last_gc_++; 143 if (requests_since_last_gc_ < kRequestsBetweenCollecting) 144 return; 145 requests_since_last_gc_ = 0; 146 147 GarbageCollectEntries(); 148 } 149 150 void URLRequestThrottlerManager::GarbageCollectEntries() { 151 UrlEntryMap::iterator i = url_entries_.begin(); 152 while (i != url_entries_.end()) { 153 if ((i->second)->IsEntryOutdated()) { 154 url_entries_.erase(i++); 155 } else { 156 ++i; 157 } 158 } 159 160 // In case something broke we want to make sure not to grow indefinitely. 161 while (url_entries_.size() > kMaximumNumberOfEntries) { 162 url_entries_.erase(url_entries_.begin()); 163 } 164 } 165 166 } // namespace net 167