blob: 16d9501b015177b2ef8650a795a84359f42d96a1 [file] [log] [blame]
xunjieli413a68782015-06-16 17:15:431// 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 "extensions/browser/extension_throttle_manager.h"
6
dchenge59eca1602015-12-18 17:48:007#include <utility>
8
xunjieli413a68782015-06-16 17:15:439#include "base/logging.h"
10#include "base/metrics/field_trial.h"
11#include "base/metrics/histogram.h"
12#include "base/strings/string_util.h"
13#include "extensions/browser/extension_request_limiting_throttle.h"
14#include "extensions/common/constants.h"
tfarina7ba5a622016-02-23 23:21:4415#include "net/base/url_util.h"
xunjieli413a68782015-06-16 17:15:4316#include "net/log/net_log.h"
17#include "net/url_request/url_request.h"
18
19namespace extensions {
20
21const unsigned int ExtensionThrottleManager::kMaximumNumberOfEntries = 1500;
22const unsigned int ExtensionThrottleManager::kRequestsBetweenCollecting = 200;
23
24ExtensionThrottleManager::ExtensionThrottleManager()
25 : requests_since_last_gc_(0),
26 enable_thread_checks_(false),
27 logged_for_localhost_disabled_(false),
28 registered_from_thread_(base::kInvalidThreadId),
29 ignore_user_gesture_load_flag_for_tests_(false) {
30 url_id_replacements_.ClearPassword();
31 url_id_replacements_.ClearUsername();
32 url_id_replacements_.ClearQuery();
33 url_id_replacements_.ClearRef();
34
35 net::NetworkChangeNotifier::AddIPAddressObserver(this);
36 net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
37}
38
39ExtensionThrottleManager::~ExtensionThrottleManager() {
40 net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
41 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
42
43 // Since the manager object might conceivably go away before the
44 // entries, detach the entries' back-pointer to the manager.
45 UrlEntryMap::iterator i = url_entries_.begin();
46 while (i != url_entries_.end()) {
47 if (i->second.get() != NULL) {
48 i->second->DetachManager();
49 }
50 ++i;
51 }
52
53 // Delete all entries.
54 url_entries_.clear();
55}
56
57scoped_ptr<content::ResourceThrottle>
58ExtensionThrottleManager::MaybeCreateThrottle(const net::URLRequest* request) {
59 if (request->first_party_for_cookies().scheme() !=
60 extensions::kExtensionScheme) {
61 return nullptr;
62 }
63 return make_scoped_ptr(
64 new extensions::ExtensionRequestLimitingThrottle(request, this));
65}
66
67scoped_refptr<ExtensionThrottleEntryInterface>
68ExtensionThrottleManager::RegisterRequestUrl(const GURL& url) {
69 DCHECK(!enable_thread_checks_ || CalledOnValidThread());
70
71 // Normalize the url.
72 std::string url_id = GetIdFromUrl(url);
73
74 // Periodically garbage collect old entries.
75 GarbageCollectEntriesIfNecessary();
76
77 // Find the entry in the map or create a new NULL entry.
78 scoped_refptr<ExtensionThrottleEntry>& entry = url_entries_[url_id];
79
80 // If the entry exists but could be garbage collected at this point, we
81 // start with a fresh entry so that we possibly back off a bit less
82 // aggressively (i.e. this resets the error count when the entry's URL
83 // hasn't been requested in long enough).
84 if (entry.get() && entry->IsEntryOutdated()) {
85 entry = NULL;
86 }
87
88 // Create the entry if needed.
89 if (entry.get() == NULL) {
xunjielie484e2d62015-06-19 14:31:2190 if (backoff_policy_for_tests_) {
91 entry = new ExtensionThrottleEntry(
92 this, url_id, backoff_policy_for_tests_.get(),
93 ignore_user_gesture_load_flag_for_tests_);
94 } else {
95 entry = new ExtensionThrottleEntry(
96 this, url_id, ignore_user_gesture_load_flag_for_tests_);
97 }
xunjieli413a68782015-06-16 17:15:4398
99 // We only disable back-off throttling on an entry that we have
100 // just constructed. This is to allow unit tests to explicitly override
101 // the entry for localhost URLs.
102 std::string host = url.host();
103 if (net::IsLocalhost(host)) {
104 if (!logged_for_localhost_disabled_ && net::IsLocalhost(host)) {
105 logged_for_localhost_disabled_ = true;
106 net_log_.AddEvent(net::NetLog::TYPE_THROTTLING_DISABLED_FOR_HOST,
107 net::NetLog::StringCallback("host", &host));
108 }
109
110 // TODO(joi): Once sliding window is separate from back-off throttling,
111 // we can simply return a dummy implementation of
112 // ExtensionThrottleEntryInterface here that never blocks anything.
113 entry->DisableBackoffThrottling();
114 }
115 }
116
117 return entry;
118}
119
xunjielie484e2d62015-06-19 14:31:21120void ExtensionThrottleManager::SetBackoffPolicyForTests(
121 scoped_ptr<net::BackoffEntry::Policy> policy) {
dchenge59eca1602015-12-18 17:48:00122 backoff_policy_for_tests_ = std::move(policy);
xunjielie484e2d62015-06-19 14:31:21123}
124
xunjieli413a68782015-06-16 17:15:43125void ExtensionThrottleManager::OverrideEntryForTests(
126 const GURL& url,
127 ExtensionThrottleEntry* entry) {
128 // Normalize the url.
129 std::string url_id = GetIdFromUrl(url);
130
131 // Periodically garbage collect old entries.
132 GarbageCollectEntriesIfNecessary();
133
134 url_entries_[url_id] = entry;
135}
136
137void ExtensionThrottleManager::EraseEntryForTests(const GURL& url) {
138 // Normalize the url.
139 std::string url_id = GetIdFromUrl(url);
140 url_entries_.erase(url_id);
141}
142
143void ExtensionThrottleManager::SetIgnoreUserGestureLoadFlagForTests(
144 bool ignore_user_gesture_load_flag_for_tests) {
145 ignore_user_gesture_load_flag_for_tests_ = true;
146}
147
148void ExtensionThrottleManager::set_enable_thread_checks(bool enable) {
149 enable_thread_checks_ = enable;
150}
151
152bool ExtensionThrottleManager::enable_thread_checks() const {
153 return enable_thread_checks_;
154}
155
156void ExtensionThrottleManager::set_net_log(net::NetLog* net_log) {
157 DCHECK(net_log);
158 net_log_ = net::BoundNetLog::Make(
159 net_log, net::NetLog::SOURCE_EXPONENTIAL_BACKOFF_THROTTLING);
160}
161
162net::NetLog* ExtensionThrottleManager::net_log() const {
163 return net_log_.net_log();
164}
165
166void ExtensionThrottleManager::OnIPAddressChanged() {
167 OnNetworkChange();
168}
169
170void ExtensionThrottleManager::OnConnectionTypeChanged(
171 net::NetworkChangeNotifier::ConnectionType type) {
172 OnNetworkChange();
173}
174
175std::string ExtensionThrottleManager::GetIdFromUrl(const GURL& url) const {
176 if (!url.is_valid())
177 return url.possibly_invalid_spec();
178
179 GURL id = url.ReplaceComponents(url_id_replacements_);
brettw8e2106d2015-08-11 19:30:22180 return base::ToLowerASCII(id.spec());
xunjieli413a68782015-06-16 17:15:43181}
182
183void ExtensionThrottleManager::GarbageCollectEntriesIfNecessary() {
184 requests_since_last_gc_++;
185 if (requests_since_last_gc_ < kRequestsBetweenCollecting)
186 return;
187 requests_since_last_gc_ = 0;
188
189 GarbageCollectEntries();
190}
191
192void ExtensionThrottleManager::GarbageCollectEntries() {
193 UrlEntryMap::iterator i = url_entries_.begin();
194 while (i != url_entries_.end()) {
195 if ((i->second)->IsEntryOutdated()) {
196 url_entries_.erase(i++);
197 } else {
198 ++i;
199 }
200 }
201
202 // In case something broke we want to make sure not to grow indefinitely.
203 while (url_entries_.size() > kMaximumNumberOfEntries) {
204 url_entries_.erase(url_entries_.begin());
205 }
206}
207
208void ExtensionThrottleManager::OnNetworkChange() {
209 // Remove all entries. Any entries that in-flight requests have a reference
210 // to will live until those requests end, and these entries may be
211 // inconsistent with new entries for the same URLs, but since what we
212 // want is a clean slate for the new connection type, this is OK.
213 url_entries_.clear();
214 requests_since_last_gc_ = 0;
215}
216
217} // namespace extensions