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.
5 #include "extensions/browser/extension_throttle_manager.h"
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 "extensions/browser/extension_request_limiting_throttle.h"
12 #include "extensions/common/constants.h"
13 #include "net/base/net_util.h"
14 #include "net/log/net_log.h"
15 #include "net/url_request/url_request.h"
17 namespace extensions
{
19 const unsigned int ExtensionThrottleManager::kMaximumNumberOfEntries
= 1500;
20 const unsigned int ExtensionThrottleManager::kRequestsBetweenCollecting
= 200;
22 ExtensionThrottleManager::ExtensionThrottleManager()
23 : requests_since_last_gc_(0),
24 enable_thread_checks_(false),
25 logged_for_localhost_disabled_(false),
26 registered_from_thread_(base::kInvalidThreadId
),
27 ignore_user_gesture_load_flag_for_tests_(false) {
28 url_id_replacements_
.ClearPassword();
29 url_id_replacements_
.ClearUsername();
30 url_id_replacements_
.ClearQuery();
31 url_id_replacements_
.ClearRef();
33 net::NetworkChangeNotifier::AddIPAddressObserver(this);
34 net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
37 ExtensionThrottleManager::~ExtensionThrottleManager() {
38 net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
39 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
41 // Since the manager object might conceivably go away before the
42 // entries, detach the entries' back-pointer to the manager.
43 UrlEntryMap::iterator i
= url_entries_
.begin();
44 while (i
!= url_entries_
.end()) {
45 if (i
->second
.get() != NULL
) {
46 i
->second
->DetachManager();
51 // Delete all entries.
55 scoped_ptr
<content::ResourceThrottle
>
56 ExtensionThrottleManager::MaybeCreateThrottle(const net::URLRequest
* request
) {
57 if (request
->first_party_for_cookies().scheme() !=
58 extensions::kExtensionScheme
) {
61 return make_scoped_ptr(
62 new extensions::ExtensionRequestLimitingThrottle(request
, this));
65 scoped_refptr
<ExtensionThrottleEntryInterface
>
66 ExtensionThrottleManager::RegisterRequestUrl(const GURL
& url
) {
67 DCHECK(!enable_thread_checks_
|| CalledOnValidThread());
70 std::string url_id
= GetIdFromUrl(url
);
72 // Periodically garbage collect old entries.
73 GarbageCollectEntriesIfNecessary();
75 // Find the entry in the map or create a new NULL entry.
76 scoped_refptr
<ExtensionThrottleEntry
>& entry
= url_entries_
[url_id
];
78 // If the entry exists but could be garbage collected at this point, we
79 // start with a fresh entry so that we possibly back off a bit less
80 // aggressively (i.e. this resets the error count when the entry's URL
81 // hasn't been requested in long enough).
82 if (entry
.get() && entry
->IsEntryOutdated()) {
86 // Create the entry if needed.
87 if (entry
.get() == NULL
) {
88 if (backoff_policy_for_tests_
) {
89 entry
= new ExtensionThrottleEntry(
90 this, url_id
, backoff_policy_for_tests_
.get(),
91 ignore_user_gesture_load_flag_for_tests_
);
93 entry
= new ExtensionThrottleEntry(
94 this, url_id
, ignore_user_gesture_load_flag_for_tests_
);
97 // We only disable back-off throttling on an entry that we have
98 // just constructed. This is to allow unit tests to explicitly override
99 // the entry for localhost URLs.
100 std::string host
= url
.host();
101 if (net::IsLocalhost(host
)) {
102 if (!logged_for_localhost_disabled_
&& net::IsLocalhost(host
)) {
103 logged_for_localhost_disabled_
= true;
104 net_log_
.AddEvent(net::NetLog::TYPE_THROTTLING_DISABLED_FOR_HOST
,
105 net::NetLog::StringCallback("host", &host
));
108 // TODO(joi): Once sliding window is separate from back-off throttling,
109 // we can simply return a dummy implementation of
110 // ExtensionThrottleEntryInterface here that never blocks anything.
111 entry
->DisableBackoffThrottling();
118 void ExtensionThrottleManager::SetBackoffPolicyForTests(
119 scoped_ptr
<net::BackoffEntry::Policy
> policy
) {
120 backoff_policy_for_tests_
= policy
.Pass();
123 void ExtensionThrottleManager::OverrideEntryForTests(
125 ExtensionThrottleEntry
* entry
) {
126 // Normalize the url.
127 std::string url_id
= GetIdFromUrl(url
);
129 // Periodically garbage collect old entries.
130 GarbageCollectEntriesIfNecessary();
132 url_entries_
[url_id
] = entry
;
135 void ExtensionThrottleManager::EraseEntryForTests(const GURL
& url
) {
136 // Normalize the url.
137 std::string url_id
= GetIdFromUrl(url
);
138 url_entries_
.erase(url_id
);
141 void ExtensionThrottleManager::SetIgnoreUserGestureLoadFlagForTests(
142 bool ignore_user_gesture_load_flag_for_tests
) {
143 ignore_user_gesture_load_flag_for_tests_
= true;
146 void ExtensionThrottleManager::set_enable_thread_checks(bool enable
) {
147 enable_thread_checks_
= enable
;
150 bool ExtensionThrottleManager::enable_thread_checks() const {
151 return enable_thread_checks_
;
154 void ExtensionThrottleManager::set_net_log(net::NetLog
* net_log
) {
156 net_log_
= net::BoundNetLog::Make(
157 net_log
, net::NetLog::SOURCE_EXPONENTIAL_BACKOFF_THROTTLING
);
160 net::NetLog
* ExtensionThrottleManager::net_log() const {
161 return net_log_
.net_log();
164 void ExtensionThrottleManager::OnIPAddressChanged() {
168 void ExtensionThrottleManager::OnConnectionTypeChanged(
169 net::NetworkChangeNotifier::ConnectionType type
) {
173 std::string
ExtensionThrottleManager::GetIdFromUrl(const GURL
& url
) const {
175 return url
.possibly_invalid_spec();
177 GURL id
= url
.ReplaceComponents(url_id_replacements_
);
178 return base::StringToLowerASCII(id
.spec()).c_str();
181 void ExtensionThrottleManager::GarbageCollectEntriesIfNecessary() {
182 requests_since_last_gc_
++;
183 if (requests_since_last_gc_
< kRequestsBetweenCollecting
)
185 requests_since_last_gc_
= 0;
187 GarbageCollectEntries();
190 void ExtensionThrottleManager::GarbageCollectEntries() {
191 UrlEntryMap::iterator i
= url_entries_
.begin();
192 while (i
!= url_entries_
.end()) {
193 if ((i
->second
)->IsEntryOutdated()) {
194 url_entries_
.erase(i
++);
200 // In case something broke we want to make sure not to grow indefinitely.
201 while (url_entries_
.size() > kMaximumNumberOfEntries
) {
202 url_entries_
.erase(url_entries_
.begin());
206 void ExtensionThrottleManager::OnNetworkChange() {
207 // Remove all entries. Any entries that in-flight requests have a reference
208 // to will live until those requests end, and these entries may be
209 // inconsistent with new entries for the same URLs, but since what we
210 // want is a clean slate for the new connection type, this is OK.
211 url_entries_
.clear();
212 requests_since_last_gc_
= 0;
215 } // namespace extensions