1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/renderer/extension_throttle_manager.h"
#include <utility>
#include "base/containers/cxx20_erase.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram.h"
#include "base/strings/string_util.h"
#include "extensions/common/constants.h"
#include "extensions/renderer/extension_url_loader_throttle.h"
#include "net/base/url_util.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "third_party/blink/public/platform/web_url.h"
#include "third_party/blink/public/platform/web_url_request.h"
namespace extensions {
const unsigned int ExtensionThrottleManager::kMaximumNumberOfEntries = 1500;
const unsigned int ExtensionThrottleManager::kRequestsBetweenCollecting = 200;
ExtensionThrottleManager::ExtensionThrottleManager()
: requests_since_last_gc_(0) {
url_id_replacements_.ClearPassword();
url_id_replacements_.ClearUsername();
url_id_replacements_.ClearQuery();
url_id_replacements_.ClearRef();
}
ExtensionThrottleManager::~ExtensionThrottleManager() {
base::AutoLock auto_lock(lock_);
// Delete all entries.
url_entries_.clear();
}
std::unique_ptr<blink::URLLoaderThrottle>
ExtensionThrottleManager::MaybeCreateURLLoaderThrottle(
const blink::WebURLRequest& request) {
// TODO(https://crbug.com/1039700): This relies on the extension scheme
// getting special handling via ShouldTreatURLSchemeAsFirstPartyWhenTopLevel,
// which has problems. Once that's removed this should probably look at top
// level directly instead.
if (request.SiteForCookies().scheme() != extensions::kExtensionScheme)
return nullptr;
return std::make_unique<ExtensionURLLoaderThrottle>(this);
}
ExtensionThrottleEntry* ExtensionThrottleManager::RegisterRequestUrl(
const GURL& url) {
// Internal function, no locking.
// 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.
std::unique_ptr<ExtensionThrottleEntry>& 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 && entry->IsEntryOutdated())
entry.reset();
// Create the entry if needed.
if (!entry) {
if (backoff_policy_for_tests_) {
entry = std::make_unique<ExtensionThrottleEntry>(
url_id, backoff_policy_for_tests_.get());
} else {
entry = std::make_unique<ExtensionThrottleEntry>(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 (net::IsLocalhost(url)) {
// TODO(joi): Once sliding window is separate from back-off throttling,
// we can simply return a dummy implementation of
// ExtensionThrottleEntry here that never blocks anything.
entry->DisableBackoffThrottling();
}
}
return entry.get();
}
bool ExtensionThrottleManager::ShouldRejectRequest(const GURL& request_url) {
base::AutoLock auto_lock(lock_);
return RegisterRequestUrl(request_url)->ShouldRejectRequest();
}
bool ExtensionThrottleManager::ShouldRejectRedirect(
const GURL& request_url,
const net::RedirectInfo& redirect_info) {
{
// An entry GC when requests are outstanding can purge entries so check
// before use.
base::AutoLock auto_lock(lock_);
auto it = url_entries_.find(GetIdFromUrl(request_url));
if (it != url_entries_.end())
it->second->UpdateWithResponse(redirect_info.status_code);
}
return ShouldRejectRequest(redirect_info.new_url);
}
void ExtensionThrottleManager::WillProcessResponse(
const GURL& response_url,
const network::mojom::URLResponseHead& response_head) {
if (response_head.network_accessed) {
// An entry GC when requests are outstanding can purge entries so check
// before use.
base::AutoLock auto_lock(lock_);
auto it = url_entries_.find(GetIdFromUrl(response_url));
if (it != url_entries_.end())
it->second->UpdateWithResponse(response_head.headers->response_code());
}
}
void ExtensionThrottleManager::SetBackoffPolicyForTests(
std::unique_ptr<net::BackoffEntry::Policy> policy) {
base::AutoLock auto_lock(lock_);
backoff_policy_for_tests_ = std::move(policy);
}
void ExtensionThrottleManager::OverrideEntryForTests(
const GURL& url,
std::unique_ptr<ExtensionThrottleEntry> entry) {
base::AutoLock auto_lock(lock_);
// Normalize the url.
std::string url_id = GetIdFromUrl(url);
// Periodically garbage collect old entries.
GarbageCollectEntriesIfNecessary();
url_entries_[url_id] = std::move(entry);
}
void ExtensionThrottleManager::EraseEntryForTests(const GURL& url) {
base::AutoLock auto_lock(lock_);
// Normalize the url.
std::string url_id = GetIdFromUrl(url);
url_entries_.erase(url_id);
}
void ExtensionThrottleManager::SetOnline(bool is_online) {
// When we switch from online to offline or change IP addresses, we
// clear all back-off history. This is a precaution in case the change in
// online state now lets us communicate without error with servers that
// we were previously getting 500 or 503 responses from (perhaps the
// responses are from a badly-written proxy that should have returned a
// 502 or 504 because it's upstream connection was down or it had no route
// to the server).
// 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.
base::AutoLock auto_lock(lock_);
url_entries_.clear();
requests_since_last_gc_ = 0;
}
std::string ExtensionThrottleManager::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 ExtensionThrottleManager::GarbageCollectEntriesIfNecessary() {
requests_since_last_gc_++;
if (requests_since_last_gc_ < kRequestsBetweenCollecting)
return;
requests_since_last_gc_ = 0;
GarbageCollectEntries();
}
void ExtensionThrottleManager::GarbageCollectEntries() {
base::EraseIf(url_entries_, [](const auto& entry) {
return entry.second->IsEntryOutdated();
});
// In case something broke we want to make sure not to grow indefinitely.
while (url_entries_.size() > kMaximumNumberOfEntries) {
url_entries_.erase(url_entries_.begin());
}
}
} // namespace extensions
|