1*6777b538SAndroid Build Coastguard Worker // Copyright 2011 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker #include "net/http/http_auth_cache.h"
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker #include <list>
8*6777b538SAndroid Build Coastguard Worker #include <map>
9*6777b538SAndroid Build Coastguard Worker
10*6777b538SAndroid Build Coastguard Worker #include "base/logging.h"
11*6777b538SAndroid Build Coastguard Worker #include "base/memory/raw_ref.h"
12*6777b538SAndroid Build Coastguard Worker #include "base/metrics/histogram_macros.h"
13*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_util.h"
14*6777b538SAndroid Build Coastguard Worker #include "url/gurl.h"
15*6777b538SAndroid Build Coastguard Worker #include "url/scheme_host_port.h"
16*6777b538SAndroid Build Coastguard Worker #include "url/url_constants.h"
17*6777b538SAndroid Build Coastguard Worker
18*6777b538SAndroid Build Coastguard Worker namespace {
19*6777b538SAndroid Build Coastguard Worker
20*6777b538SAndroid Build Coastguard Worker // Helper to find the containing directory of path. In RFC 2617 this is what
21*6777b538SAndroid Build Coastguard Worker // they call the "last symbolic element in the absolute path".
22*6777b538SAndroid Build Coastguard Worker // Examples:
23*6777b538SAndroid Build Coastguard Worker // "/foo/bar.txt" --> "/foo/"
24*6777b538SAndroid Build Coastguard Worker // "/foo/" --> "/foo/"
GetParentDirectory(const std::string & path)25*6777b538SAndroid Build Coastguard Worker std::string GetParentDirectory(const std::string& path) {
26*6777b538SAndroid Build Coastguard Worker std::string::size_type last_slash = path.rfind("/");
27*6777b538SAndroid Build Coastguard Worker if (last_slash == std::string::npos) {
28*6777b538SAndroid Build Coastguard Worker // No slash (absolute paths always start with slash, so this must be
29*6777b538SAndroid Build Coastguard Worker // the proxy case which uses empty string).
30*6777b538SAndroid Build Coastguard Worker DCHECK(path.empty());
31*6777b538SAndroid Build Coastguard Worker return path;
32*6777b538SAndroid Build Coastguard Worker }
33*6777b538SAndroid Build Coastguard Worker return path.substr(0, last_slash + 1);
34*6777b538SAndroid Build Coastguard Worker }
35*6777b538SAndroid Build Coastguard Worker
36*6777b538SAndroid Build Coastguard Worker // Return true if |path| is a subpath of |container|. In other words, is
37*6777b538SAndroid Build Coastguard Worker // |container| an ancestor of |path|?
IsEnclosingPath(const std::string & container,const std::string & path)38*6777b538SAndroid Build Coastguard Worker bool IsEnclosingPath(const std::string& container, const std::string& path) {
39*6777b538SAndroid Build Coastguard Worker DCHECK(container.empty() || *(container.end() - 1) == '/');
40*6777b538SAndroid Build Coastguard Worker return ((container.empty() && path.empty()) ||
41*6777b538SAndroid Build Coastguard Worker (!container.empty() && path.starts_with(container)));
42*6777b538SAndroid Build Coastguard Worker }
43*6777b538SAndroid Build Coastguard Worker
44*6777b538SAndroid Build Coastguard Worker #if DCHECK_IS_ON()
45*6777b538SAndroid Build Coastguard Worker // Debug helper to check that |scheme_host_port| arguments are properly formed.
CheckSchemeHostPortIsValid(const url::SchemeHostPort & scheme_host_port)46*6777b538SAndroid Build Coastguard Worker void CheckSchemeHostPortIsValid(const url::SchemeHostPort& scheme_host_port) {
47*6777b538SAndroid Build Coastguard Worker DCHECK(scheme_host_port.IsValid());
48*6777b538SAndroid Build Coastguard Worker DCHECK(scheme_host_port.scheme() == url::kHttpScheme ||
49*6777b538SAndroid Build Coastguard Worker scheme_host_port.scheme() == url::kHttpsScheme ||
50*6777b538SAndroid Build Coastguard Worker scheme_host_port.scheme() == url::kWsScheme ||
51*6777b538SAndroid Build Coastguard Worker scheme_host_port.scheme() == url::kWssScheme);
52*6777b538SAndroid Build Coastguard Worker }
53*6777b538SAndroid Build Coastguard Worker
54*6777b538SAndroid Build Coastguard Worker // Debug helper to check that |path| arguments are properly formed.
55*6777b538SAndroid Build Coastguard Worker // (should be absolute path, or empty string).
CheckPathIsValid(const std::string & path)56*6777b538SAndroid Build Coastguard Worker void CheckPathIsValid(const std::string& path) {
57*6777b538SAndroid Build Coastguard Worker DCHECK(path.empty() || path[0] == '/');
58*6777b538SAndroid Build Coastguard Worker }
59*6777b538SAndroid Build Coastguard Worker #endif
60*6777b538SAndroid Build Coastguard Worker
61*6777b538SAndroid Build Coastguard Worker // Functor used by std::erase_if.
62*6777b538SAndroid Build Coastguard Worker struct IsEnclosedBy {
IsEnclosedBy__anon6afbf13e0111::IsEnclosedBy63*6777b538SAndroid Build Coastguard Worker explicit IsEnclosedBy(const std::string& path) : path(path) { }
operator ()__anon6afbf13e0111::IsEnclosedBy64*6777b538SAndroid Build Coastguard Worker bool operator() (const std::string& x) const {
65*6777b538SAndroid Build Coastguard Worker return IsEnclosingPath(*path, x);
66*6777b538SAndroid Build Coastguard Worker }
67*6777b538SAndroid Build Coastguard Worker const raw_ref<const std::string> path;
68*6777b538SAndroid Build Coastguard Worker };
69*6777b538SAndroid Build Coastguard Worker
70*6777b538SAndroid Build Coastguard Worker } // namespace
71*6777b538SAndroid Build Coastguard Worker
72*6777b538SAndroid Build Coastguard Worker namespace net {
73*6777b538SAndroid Build Coastguard Worker
HttpAuthCache(bool key_server_entries_by_network_anonymization_key)74*6777b538SAndroid Build Coastguard Worker HttpAuthCache::HttpAuthCache(
75*6777b538SAndroid Build Coastguard Worker bool key_server_entries_by_network_anonymization_key)
76*6777b538SAndroid Build Coastguard Worker : key_server_entries_by_network_anonymization_key_(
77*6777b538SAndroid Build Coastguard Worker key_server_entries_by_network_anonymization_key) {}
78*6777b538SAndroid Build Coastguard Worker
79*6777b538SAndroid Build Coastguard Worker HttpAuthCache::~HttpAuthCache() = default;
80*6777b538SAndroid Build Coastguard Worker
SetKeyServerEntriesByNetworkAnonymizationKey(bool key_server_entries_by_network_anonymization_key)81*6777b538SAndroid Build Coastguard Worker void HttpAuthCache::SetKeyServerEntriesByNetworkAnonymizationKey(
82*6777b538SAndroid Build Coastguard Worker bool key_server_entries_by_network_anonymization_key) {
83*6777b538SAndroid Build Coastguard Worker if (key_server_entries_by_network_anonymization_key_ ==
84*6777b538SAndroid Build Coastguard Worker key_server_entries_by_network_anonymization_key) {
85*6777b538SAndroid Build Coastguard Worker return;
86*6777b538SAndroid Build Coastguard Worker }
87*6777b538SAndroid Build Coastguard Worker
88*6777b538SAndroid Build Coastguard Worker key_server_entries_by_network_anonymization_key_ =
89*6777b538SAndroid Build Coastguard Worker key_server_entries_by_network_anonymization_key;
90*6777b538SAndroid Build Coastguard Worker std::erase_if(entries_, [](const EntryMap::value_type& entry_map_pair) {
91*6777b538SAndroid Build Coastguard Worker return entry_map_pair.first.target == HttpAuth::AUTH_SERVER;
92*6777b538SAndroid Build Coastguard Worker });
93*6777b538SAndroid Build Coastguard Worker }
94*6777b538SAndroid Build Coastguard Worker
95*6777b538SAndroid Build Coastguard Worker // Performance: O(logN+n), where N is the total number of entries, n is the
96*6777b538SAndroid Build Coastguard Worker // number of realm entries for the given SchemeHostPort, target, and with a
97*6777b538SAndroid Build Coastguard Worker // matching NetworkAnonymizationKey.
Lookup(const url::SchemeHostPort & scheme_host_port,HttpAuth::Target target,const std::string & realm,HttpAuth::Scheme scheme,const NetworkAnonymizationKey & network_anonymization_key)98*6777b538SAndroid Build Coastguard Worker HttpAuthCache::Entry* HttpAuthCache::Lookup(
99*6777b538SAndroid Build Coastguard Worker const url::SchemeHostPort& scheme_host_port,
100*6777b538SAndroid Build Coastguard Worker HttpAuth::Target target,
101*6777b538SAndroid Build Coastguard Worker const std::string& realm,
102*6777b538SAndroid Build Coastguard Worker HttpAuth::Scheme scheme,
103*6777b538SAndroid Build Coastguard Worker const NetworkAnonymizationKey& network_anonymization_key) {
104*6777b538SAndroid Build Coastguard Worker EntryMap::iterator entry_it = LookupEntryIt(
105*6777b538SAndroid Build Coastguard Worker scheme_host_port, target, realm, scheme, network_anonymization_key);
106*6777b538SAndroid Build Coastguard Worker if (entry_it == entries_.end())
107*6777b538SAndroid Build Coastguard Worker return nullptr;
108*6777b538SAndroid Build Coastguard Worker return &(entry_it->second);
109*6777b538SAndroid Build Coastguard Worker }
110*6777b538SAndroid Build Coastguard Worker
111*6777b538SAndroid Build Coastguard Worker // Performance: O(logN+n*m), where N is the total number of entries, n is the
112*6777b538SAndroid Build Coastguard Worker // number of realm entries for the given SchemeHostPort, target, and
113*6777b538SAndroid Build Coastguard Worker // NetworkAnonymizationKey, m is the number of path entries per realm. Both n
114*6777b538SAndroid Build Coastguard Worker // and m are expected to be small; m is kept small because AddPath() only keeps
115*6777b538SAndroid Build Coastguard Worker // the shallowest entry.
LookupByPath(const url::SchemeHostPort & scheme_host_port,HttpAuth::Target target,const NetworkAnonymizationKey & network_anonymization_key,const std::string & path)116*6777b538SAndroid Build Coastguard Worker HttpAuthCache::Entry* HttpAuthCache::LookupByPath(
117*6777b538SAndroid Build Coastguard Worker const url::SchemeHostPort& scheme_host_port,
118*6777b538SAndroid Build Coastguard Worker HttpAuth::Target target,
119*6777b538SAndroid Build Coastguard Worker const NetworkAnonymizationKey& network_anonymization_key,
120*6777b538SAndroid Build Coastguard Worker const std::string& path) {
121*6777b538SAndroid Build Coastguard Worker #if DCHECK_IS_ON()
122*6777b538SAndroid Build Coastguard Worker CheckSchemeHostPortIsValid(scheme_host_port);
123*6777b538SAndroid Build Coastguard Worker CheckPathIsValid(path);
124*6777b538SAndroid Build Coastguard Worker #endif
125*6777b538SAndroid Build Coastguard Worker
126*6777b538SAndroid Build Coastguard Worker // RFC 2617 section 2:
127*6777b538SAndroid Build Coastguard Worker // A client SHOULD assume that all paths at or deeper than the depth of
128*6777b538SAndroid Build Coastguard Worker // the last symbolic element in the path field of the Request-URI also are
129*6777b538SAndroid Build Coastguard Worker // within the protection space ...
130*6777b538SAndroid Build Coastguard Worker std::string parent_dir = GetParentDirectory(path);
131*6777b538SAndroid Build Coastguard Worker
132*6777b538SAndroid Build Coastguard Worker // Linear scan through the <scheme, realm> entries for the given
133*6777b538SAndroid Build Coastguard Worker // SchemeHostPort.
134*6777b538SAndroid Build Coastguard Worker auto entry_range = entries_.equal_range(
135*6777b538SAndroid Build Coastguard Worker EntryMapKey(scheme_host_port, target, network_anonymization_key,
136*6777b538SAndroid Build Coastguard Worker key_server_entries_by_network_anonymization_key_));
137*6777b538SAndroid Build Coastguard Worker auto best_match_it = entries_.end();
138*6777b538SAndroid Build Coastguard Worker size_t best_match_length = 0;
139*6777b538SAndroid Build Coastguard Worker for (auto it = entry_range.first; it != entry_range.second; ++it) {
140*6777b538SAndroid Build Coastguard Worker size_t len = 0;
141*6777b538SAndroid Build Coastguard Worker auto& entry = it->second;
142*6777b538SAndroid Build Coastguard Worker DCHECK(entry.scheme_host_port() == scheme_host_port);
143*6777b538SAndroid Build Coastguard Worker if (entry.HasEnclosingPath(parent_dir, &len) &&
144*6777b538SAndroid Build Coastguard Worker (best_match_it == entries_.end() || len > best_match_length)) {
145*6777b538SAndroid Build Coastguard Worker best_match_it = it;
146*6777b538SAndroid Build Coastguard Worker best_match_length = len;
147*6777b538SAndroid Build Coastguard Worker }
148*6777b538SAndroid Build Coastguard Worker }
149*6777b538SAndroid Build Coastguard Worker if (best_match_it != entries_.end()) {
150*6777b538SAndroid Build Coastguard Worker Entry& best_match_entry = best_match_it->second;
151*6777b538SAndroid Build Coastguard Worker best_match_entry.last_use_time_ticks_ = tick_clock_->NowTicks();
152*6777b538SAndroid Build Coastguard Worker return &best_match_entry;
153*6777b538SAndroid Build Coastguard Worker }
154*6777b538SAndroid Build Coastguard Worker return nullptr;
155*6777b538SAndroid Build Coastguard Worker }
156*6777b538SAndroid Build Coastguard Worker
Add(const url::SchemeHostPort & scheme_host_port,HttpAuth::Target target,const std::string & realm,HttpAuth::Scheme scheme,const NetworkAnonymizationKey & network_anonymization_key,const std::string & auth_challenge,const AuthCredentials & credentials,const std::string & path)157*6777b538SAndroid Build Coastguard Worker HttpAuthCache::Entry* HttpAuthCache::Add(
158*6777b538SAndroid Build Coastguard Worker const url::SchemeHostPort& scheme_host_port,
159*6777b538SAndroid Build Coastguard Worker HttpAuth::Target target,
160*6777b538SAndroid Build Coastguard Worker const std::string& realm,
161*6777b538SAndroid Build Coastguard Worker HttpAuth::Scheme scheme,
162*6777b538SAndroid Build Coastguard Worker const NetworkAnonymizationKey& network_anonymization_key,
163*6777b538SAndroid Build Coastguard Worker const std::string& auth_challenge,
164*6777b538SAndroid Build Coastguard Worker const AuthCredentials& credentials,
165*6777b538SAndroid Build Coastguard Worker const std::string& path) {
166*6777b538SAndroid Build Coastguard Worker #if DCHECK_IS_ON()
167*6777b538SAndroid Build Coastguard Worker CheckSchemeHostPortIsValid(scheme_host_port);
168*6777b538SAndroid Build Coastguard Worker CheckPathIsValid(path);
169*6777b538SAndroid Build Coastguard Worker #endif
170*6777b538SAndroid Build Coastguard Worker
171*6777b538SAndroid Build Coastguard Worker base::TimeTicks now_ticks = tick_clock_->NowTicks();
172*6777b538SAndroid Build Coastguard Worker
173*6777b538SAndroid Build Coastguard Worker // Check for existing entry (we will re-use it if present).
174*6777b538SAndroid Build Coastguard Worker HttpAuthCache::Entry* entry = Lookup(scheme_host_port, target, realm, scheme,
175*6777b538SAndroid Build Coastguard Worker network_anonymization_key);
176*6777b538SAndroid Build Coastguard Worker if (!entry) {
177*6777b538SAndroid Build Coastguard Worker // Failsafe to prevent unbounded memory growth of the cache.
178*6777b538SAndroid Build Coastguard Worker //
179*6777b538SAndroid Build Coastguard Worker // Data was collected in June of 2019, before entries were keyed on either
180*6777b538SAndroid Build Coastguard Worker // HttpAuth::Target or NetworkAnonymizationKey. That data indicated that the
181*6777b538SAndroid Build Coastguard Worker // eviction rate was at around 0.05%. I.e. 0.05% of the time the number of
182*6777b538SAndroid Build Coastguard Worker // entries in the cache exceed kMaxNumRealmEntries. The evicted entry is
183*6777b538SAndroid Build Coastguard Worker // roughly half an hour old (median), and it's been around 25 minutes since
184*6777b538SAndroid Build Coastguard Worker // its last use (median).
185*6777b538SAndroid Build Coastguard Worker if (entries_.size() >= kMaxNumRealmEntries) {
186*6777b538SAndroid Build Coastguard Worker DLOG(WARNING) << "Num auth cache entries reached limit -- evicting";
187*6777b538SAndroid Build Coastguard Worker EvictLeastRecentlyUsedEntry();
188*6777b538SAndroid Build Coastguard Worker }
189*6777b538SAndroid Build Coastguard Worker entry =
190*6777b538SAndroid Build Coastguard Worker &(entries_
191*6777b538SAndroid Build Coastguard Worker .insert({EntryMapKey(
192*6777b538SAndroid Build Coastguard Worker scheme_host_port, target, network_anonymization_key,
193*6777b538SAndroid Build Coastguard Worker key_server_entries_by_network_anonymization_key_),
194*6777b538SAndroid Build Coastguard Worker Entry()})
195*6777b538SAndroid Build Coastguard Worker ->second);
196*6777b538SAndroid Build Coastguard Worker entry->scheme_host_port_ = scheme_host_port;
197*6777b538SAndroid Build Coastguard Worker entry->realm_ = realm;
198*6777b538SAndroid Build Coastguard Worker entry->scheme_ = scheme;
199*6777b538SAndroid Build Coastguard Worker entry->creation_time_ticks_ = now_ticks;
200*6777b538SAndroid Build Coastguard Worker entry->creation_time_ = clock_->Now();
201*6777b538SAndroid Build Coastguard Worker }
202*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(scheme_host_port, entry->scheme_host_port_);
203*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(realm, entry->realm_);
204*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(scheme, entry->scheme_);
205*6777b538SAndroid Build Coastguard Worker
206*6777b538SAndroid Build Coastguard Worker entry->auth_challenge_ = auth_challenge;
207*6777b538SAndroid Build Coastguard Worker entry->credentials_ = credentials;
208*6777b538SAndroid Build Coastguard Worker entry->nonce_count_ = 1;
209*6777b538SAndroid Build Coastguard Worker entry->AddPath(path);
210*6777b538SAndroid Build Coastguard Worker entry->last_use_time_ticks_ = now_ticks;
211*6777b538SAndroid Build Coastguard Worker
212*6777b538SAndroid Build Coastguard Worker return entry;
213*6777b538SAndroid Build Coastguard Worker }
214*6777b538SAndroid Build Coastguard Worker
215*6777b538SAndroid Build Coastguard Worker HttpAuthCache::Entry::Entry(const Entry& other) = default;
216*6777b538SAndroid Build Coastguard Worker
217*6777b538SAndroid Build Coastguard Worker HttpAuthCache::Entry::~Entry() = default;
218*6777b538SAndroid Build Coastguard Worker
UpdateStaleChallenge(const std::string & auth_challenge)219*6777b538SAndroid Build Coastguard Worker void HttpAuthCache::Entry::UpdateStaleChallenge(
220*6777b538SAndroid Build Coastguard Worker const std::string& auth_challenge) {
221*6777b538SAndroid Build Coastguard Worker auth_challenge_ = auth_challenge;
222*6777b538SAndroid Build Coastguard Worker nonce_count_ = 1;
223*6777b538SAndroid Build Coastguard Worker }
224*6777b538SAndroid Build Coastguard Worker
IsEqualForTesting(const Entry & other) const225*6777b538SAndroid Build Coastguard Worker bool HttpAuthCache::Entry::IsEqualForTesting(const Entry& other) const {
226*6777b538SAndroid Build Coastguard Worker if (scheme_host_port() != other.scheme_host_port())
227*6777b538SAndroid Build Coastguard Worker return false;
228*6777b538SAndroid Build Coastguard Worker if (realm() != other.realm())
229*6777b538SAndroid Build Coastguard Worker return false;
230*6777b538SAndroid Build Coastguard Worker if (scheme() != other.scheme())
231*6777b538SAndroid Build Coastguard Worker return false;
232*6777b538SAndroid Build Coastguard Worker if (auth_challenge() != other.auth_challenge())
233*6777b538SAndroid Build Coastguard Worker return false;
234*6777b538SAndroid Build Coastguard Worker if (!credentials().Equals(other.credentials()))
235*6777b538SAndroid Build Coastguard Worker return false;
236*6777b538SAndroid Build Coastguard Worker std::set<std::string> lhs_paths(paths_.begin(), paths_.end());
237*6777b538SAndroid Build Coastguard Worker std::set<std::string> rhs_paths(other.paths_.begin(), other.paths_.end());
238*6777b538SAndroid Build Coastguard Worker if (lhs_paths != rhs_paths)
239*6777b538SAndroid Build Coastguard Worker return false;
240*6777b538SAndroid Build Coastguard Worker return true;
241*6777b538SAndroid Build Coastguard Worker }
242*6777b538SAndroid Build Coastguard Worker
243*6777b538SAndroid Build Coastguard Worker HttpAuthCache::Entry::Entry() = default;
244*6777b538SAndroid Build Coastguard Worker
AddPath(const std::string & path)245*6777b538SAndroid Build Coastguard Worker void HttpAuthCache::Entry::AddPath(const std::string& path) {
246*6777b538SAndroid Build Coastguard Worker std::string parent_dir = GetParentDirectory(path);
247*6777b538SAndroid Build Coastguard Worker if (!HasEnclosingPath(parent_dir, nullptr)) {
248*6777b538SAndroid Build Coastguard Worker // Remove any entries that have been subsumed by the new entry.
249*6777b538SAndroid Build Coastguard Worker std::erase_if(paths_, IsEnclosedBy(parent_dir));
250*6777b538SAndroid Build Coastguard Worker
251*6777b538SAndroid Build Coastguard Worker // Failsafe to prevent unbounded memory growth of the cache.
252*6777b538SAndroid Build Coastguard Worker //
253*6777b538SAndroid Build Coastguard Worker // Data collected on June of 2019 indicate that when we get here, the list
254*6777b538SAndroid Build Coastguard Worker // of paths has reached the 10 entry maximum around 1% of the time.
255*6777b538SAndroid Build Coastguard Worker if (paths_.size() >= kMaxNumPathsPerRealmEntry) {
256*6777b538SAndroid Build Coastguard Worker DLOG(WARNING) << "Num path entries for " << scheme_host_port()
257*6777b538SAndroid Build Coastguard Worker << " has grown too large -- evicting";
258*6777b538SAndroid Build Coastguard Worker paths_.pop_back();
259*6777b538SAndroid Build Coastguard Worker }
260*6777b538SAndroid Build Coastguard Worker
261*6777b538SAndroid Build Coastguard Worker // Add new path.
262*6777b538SAndroid Build Coastguard Worker paths_.push_front(parent_dir);
263*6777b538SAndroid Build Coastguard Worker }
264*6777b538SAndroid Build Coastguard Worker }
265*6777b538SAndroid Build Coastguard Worker
HasEnclosingPath(const std::string & dir,size_t * path_len)266*6777b538SAndroid Build Coastguard Worker bool HttpAuthCache::Entry::HasEnclosingPath(const std::string& dir,
267*6777b538SAndroid Build Coastguard Worker size_t* path_len) {
268*6777b538SAndroid Build Coastguard Worker DCHECK(GetParentDirectory(dir) == dir);
269*6777b538SAndroid Build Coastguard Worker for (PathList::iterator it = paths_.begin(); it != paths_.end(); ++it) {
270*6777b538SAndroid Build Coastguard Worker if (IsEnclosingPath(*it, dir)) {
271*6777b538SAndroid Build Coastguard Worker // No element of paths_ may enclose any other element.
272*6777b538SAndroid Build Coastguard Worker // Therefore this path is the tightest bound. Important because
273*6777b538SAndroid Build Coastguard Worker // the length returned is used to determine the cache entry that
274*6777b538SAndroid Build Coastguard Worker // has the closest enclosing path in LookupByPath().
275*6777b538SAndroid Build Coastguard Worker if (path_len)
276*6777b538SAndroid Build Coastguard Worker *path_len = it->length();
277*6777b538SAndroid Build Coastguard Worker // Move the found path up by one place so that more frequently used paths
278*6777b538SAndroid Build Coastguard Worker // migrate towards the beginning of the list of paths.
279*6777b538SAndroid Build Coastguard Worker if (it != paths_.begin())
280*6777b538SAndroid Build Coastguard Worker std::iter_swap(it, std::prev(it));
281*6777b538SAndroid Build Coastguard Worker return true;
282*6777b538SAndroid Build Coastguard Worker }
283*6777b538SAndroid Build Coastguard Worker }
284*6777b538SAndroid Build Coastguard Worker return false;
285*6777b538SAndroid Build Coastguard Worker }
286*6777b538SAndroid Build Coastguard Worker
Remove(const url::SchemeHostPort & scheme_host_port,HttpAuth::Target target,const std::string & realm,HttpAuth::Scheme scheme,const NetworkAnonymizationKey & network_anonymization_key,const AuthCredentials & credentials)287*6777b538SAndroid Build Coastguard Worker bool HttpAuthCache::Remove(
288*6777b538SAndroid Build Coastguard Worker const url::SchemeHostPort& scheme_host_port,
289*6777b538SAndroid Build Coastguard Worker HttpAuth::Target target,
290*6777b538SAndroid Build Coastguard Worker const std::string& realm,
291*6777b538SAndroid Build Coastguard Worker HttpAuth::Scheme scheme,
292*6777b538SAndroid Build Coastguard Worker const NetworkAnonymizationKey& network_anonymization_key,
293*6777b538SAndroid Build Coastguard Worker const AuthCredentials& credentials) {
294*6777b538SAndroid Build Coastguard Worker EntryMap::iterator entry_it = LookupEntryIt(
295*6777b538SAndroid Build Coastguard Worker scheme_host_port, target, realm, scheme, network_anonymization_key);
296*6777b538SAndroid Build Coastguard Worker if (entry_it == entries_.end())
297*6777b538SAndroid Build Coastguard Worker return false;
298*6777b538SAndroid Build Coastguard Worker Entry& entry = entry_it->second;
299*6777b538SAndroid Build Coastguard Worker if (credentials.Equals(entry.credentials())) {
300*6777b538SAndroid Build Coastguard Worker entries_.erase(entry_it);
301*6777b538SAndroid Build Coastguard Worker return true;
302*6777b538SAndroid Build Coastguard Worker }
303*6777b538SAndroid Build Coastguard Worker return false;
304*6777b538SAndroid Build Coastguard Worker }
305*6777b538SAndroid Build Coastguard Worker
ClearEntriesAddedBetween(base::Time begin_time,base::Time end_time,base::RepeatingCallback<bool (const GURL &)> url_matcher)306*6777b538SAndroid Build Coastguard Worker void HttpAuthCache::ClearEntriesAddedBetween(
307*6777b538SAndroid Build Coastguard Worker base::Time begin_time,
308*6777b538SAndroid Build Coastguard Worker base::Time end_time,
309*6777b538SAndroid Build Coastguard Worker base::RepeatingCallback<bool(const GURL&)> url_matcher) {
310*6777b538SAndroid Build Coastguard Worker if (begin_time.is_min() && end_time.is_max() && !url_matcher) {
311*6777b538SAndroid Build Coastguard Worker ClearAllEntries();
312*6777b538SAndroid Build Coastguard Worker return;
313*6777b538SAndroid Build Coastguard Worker }
314*6777b538SAndroid Build Coastguard Worker std::erase_if(entries_, [begin_time, end_time, url_matcher](
315*6777b538SAndroid Build Coastguard Worker const EntryMap::value_type& entry_map_pair) {
316*6777b538SAndroid Build Coastguard Worker const Entry& entry = entry_map_pair.second;
317*6777b538SAndroid Build Coastguard Worker return entry.creation_time_ >= begin_time &&
318*6777b538SAndroid Build Coastguard Worker entry.creation_time_ < end_time &&
319*6777b538SAndroid Build Coastguard Worker (url_matcher ? url_matcher.Run(entry.scheme_host_port().GetURL())
320*6777b538SAndroid Build Coastguard Worker : true);
321*6777b538SAndroid Build Coastguard Worker });
322*6777b538SAndroid Build Coastguard Worker }
323*6777b538SAndroid Build Coastguard Worker
ClearAllEntries()324*6777b538SAndroid Build Coastguard Worker void HttpAuthCache::ClearAllEntries() {
325*6777b538SAndroid Build Coastguard Worker entries_.clear();
326*6777b538SAndroid Build Coastguard Worker }
327*6777b538SAndroid Build Coastguard Worker
UpdateStaleChallenge(const url::SchemeHostPort & scheme_host_port,HttpAuth::Target target,const std::string & realm,HttpAuth::Scheme scheme,const NetworkAnonymizationKey & network_anonymization_key,const std::string & auth_challenge)328*6777b538SAndroid Build Coastguard Worker bool HttpAuthCache::UpdateStaleChallenge(
329*6777b538SAndroid Build Coastguard Worker const url::SchemeHostPort& scheme_host_port,
330*6777b538SAndroid Build Coastguard Worker HttpAuth::Target target,
331*6777b538SAndroid Build Coastguard Worker const std::string& realm,
332*6777b538SAndroid Build Coastguard Worker HttpAuth::Scheme scheme,
333*6777b538SAndroid Build Coastguard Worker const NetworkAnonymizationKey& network_anonymization_key,
334*6777b538SAndroid Build Coastguard Worker const std::string& auth_challenge) {
335*6777b538SAndroid Build Coastguard Worker HttpAuthCache::Entry* entry = Lookup(scheme_host_port, target, realm, scheme,
336*6777b538SAndroid Build Coastguard Worker network_anonymization_key);
337*6777b538SAndroid Build Coastguard Worker if (!entry)
338*6777b538SAndroid Build Coastguard Worker return false;
339*6777b538SAndroid Build Coastguard Worker entry->UpdateStaleChallenge(auth_challenge);
340*6777b538SAndroid Build Coastguard Worker entry->last_use_time_ticks_ = tick_clock_->NowTicks();
341*6777b538SAndroid Build Coastguard Worker return true;
342*6777b538SAndroid Build Coastguard Worker }
343*6777b538SAndroid Build Coastguard Worker
CopyProxyEntriesFrom(const HttpAuthCache & other)344*6777b538SAndroid Build Coastguard Worker void HttpAuthCache::CopyProxyEntriesFrom(const HttpAuthCache& other) {
345*6777b538SAndroid Build Coastguard Worker for (auto it = other.entries_.begin(); it != other.entries_.end(); ++it) {
346*6777b538SAndroid Build Coastguard Worker const Entry& e = it->second;
347*6777b538SAndroid Build Coastguard Worker
348*6777b538SAndroid Build Coastguard Worker // Skip non-proxy entries.
349*6777b538SAndroid Build Coastguard Worker if (it->first.target != HttpAuth::AUTH_PROXY)
350*6777b538SAndroid Build Coastguard Worker continue;
351*6777b538SAndroid Build Coastguard Worker
352*6777b538SAndroid Build Coastguard Worker // Sanity check - proxy entries should have an empty
353*6777b538SAndroid Build Coastguard Worker // NetworkAnonymizationKey.
354*6777b538SAndroid Build Coastguard Worker DCHECK(NetworkAnonymizationKey() == it->first.network_anonymization_key);
355*6777b538SAndroid Build Coastguard Worker
356*6777b538SAndroid Build Coastguard Worker // Add an Entry with one of the original entry's paths.
357*6777b538SAndroid Build Coastguard Worker DCHECK(e.paths_.size() > 0);
358*6777b538SAndroid Build Coastguard Worker Entry* entry = Add(e.scheme_host_port(), it->first.target, e.realm(),
359*6777b538SAndroid Build Coastguard Worker e.scheme(), it->first.network_anonymization_key,
360*6777b538SAndroid Build Coastguard Worker e.auth_challenge(), e.credentials(), e.paths_.back());
361*6777b538SAndroid Build Coastguard Worker // Copy all other paths.
362*6777b538SAndroid Build Coastguard Worker for (auto it2 = std::next(e.paths_.rbegin()); it2 != e.paths_.rend(); ++it2)
363*6777b538SAndroid Build Coastguard Worker entry->AddPath(*it2);
364*6777b538SAndroid Build Coastguard Worker // Copy nonce count (for digest authentication).
365*6777b538SAndroid Build Coastguard Worker entry->nonce_count_ = e.nonce_count_;
366*6777b538SAndroid Build Coastguard Worker }
367*6777b538SAndroid Build Coastguard Worker }
368*6777b538SAndroid Build Coastguard Worker
EntryMapKey(const url::SchemeHostPort & scheme_host_port,HttpAuth::Target target,const NetworkAnonymizationKey & network_anonymization_key,bool key_server_entries_by_network_anonymization_key)369*6777b538SAndroid Build Coastguard Worker HttpAuthCache::EntryMapKey::EntryMapKey(
370*6777b538SAndroid Build Coastguard Worker const url::SchemeHostPort& scheme_host_port,
371*6777b538SAndroid Build Coastguard Worker HttpAuth::Target target,
372*6777b538SAndroid Build Coastguard Worker const NetworkAnonymizationKey& network_anonymization_key,
373*6777b538SAndroid Build Coastguard Worker bool key_server_entries_by_network_anonymization_key)
374*6777b538SAndroid Build Coastguard Worker : scheme_host_port(scheme_host_port),
375*6777b538SAndroid Build Coastguard Worker target(target),
376*6777b538SAndroid Build Coastguard Worker network_anonymization_key(
377*6777b538SAndroid Build Coastguard Worker target == HttpAuth::AUTH_SERVER &&
378*6777b538SAndroid Build Coastguard Worker key_server_entries_by_network_anonymization_key
379*6777b538SAndroid Build Coastguard Worker ? network_anonymization_key
380*6777b538SAndroid Build Coastguard Worker : NetworkAnonymizationKey()) {}
381*6777b538SAndroid Build Coastguard Worker
382*6777b538SAndroid Build Coastguard Worker HttpAuthCache::EntryMapKey::~EntryMapKey() = default;
383*6777b538SAndroid Build Coastguard Worker
operator <(const EntryMapKey & other) const384*6777b538SAndroid Build Coastguard Worker bool HttpAuthCache::EntryMapKey::operator<(const EntryMapKey& other) const {
385*6777b538SAndroid Build Coastguard Worker return std::tie(scheme_host_port, target, network_anonymization_key) <
386*6777b538SAndroid Build Coastguard Worker std::tie(other.scheme_host_port, other.target,
387*6777b538SAndroid Build Coastguard Worker other.network_anonymization_key);
388*6777b538SAndroid Build Coastguard Worker }
389*6777b538SAndroid Build Coastguard Worker
GetEntriesSizeForTesting()390*6777b538SAndroid Build Coastguard Worker size_t HttpAuthCache::GetEntriesSizeForTesting() {
391*6777b538SAndroid Build Coastguard Worker return entries_.size();
392*6777b538SAndroid Build Coastguard Worker }
393*6777b538SAndroid Build Coastguard Worker
LookupEntryIt(const url::SchemeHostPort & scheme_host_port,HttpAuth::Target target,const std::string & realm,HttpAuth::Scheme scheme,const NetworkAnonymizationKey & network_anonymization_key)394*6777b538SAndroid Build Coastguard Worker HttpAuthCache::EntryMap::iterator HttpAuthCache::LookupEntryIt(
395*6777b538SAndroid Build Coastguard Worker const url::SchemeHostPort& scheme_host_port,
396*6777b538SAndroid Build Coastguard Worker HttpAuth::Target target,
397*6777b538SAndroid Build Coastguard Worker const std::string& realm,
398*6777b538SAndroid Build Coastguard Worker HttpAuth::Scheme scheme,
399*6777b538SAndroid Build Coastguard Worker const NetworkAnonymizationKey& network_anonymization_key) {
400*6777b538SAndroid Build Coastguard Worker #if DCHECK_IS_ON()
401*6777b538SAndroid Build Coastguard Worker CheckSchemeHostPortIsValid(scheme_host_port);
402*6777b538SAndroid Build Coastguard Worker #endif
403*6777b538SAndroid Build Coastguard Worker
404*6777b538SAndroid Build Coastguard Worker // Linear scan through the <scheme, realm> entries for the given
405*6777b538SAndroid Build Coastguard Worker // SchemeHostPort and NetworkAnonymizationKey.
406*6777b538SAndroid Build Coastguard Worker auto entry_range = entries_.equal_range(
407*6777b538SAndroid Build Coastguard Worker EntryMapKey(scheme_host_port, target, network_anonymization_key,
408*6777b538SAndroid Build Coastguard Worker key_server_entries_by_network_anonymization_key_));
409*6777b538SAndroid Build Coastguard Worker for (auto it = entry_range.first; it != entry_range.second; ++it) {
410*6777b538SAndroid Build Coastguard Worker Entry& entry = it->second;
411*6777b538SAndroid Build Coastguard Worker DCHECK(entry.scheme_host_port() == scheme_host_port);
412*6777b538SAndroid Build Coastguard Worker if (entry.scheme() == scheme && entry.realm() == realm) {
413*6777b538SAndroid Build Coastguard Worker entry.last_use_time_ticks_ = tick_clock_->NowTicks();
414*6777b538SAndroid Build Coastguard Worker return it;
415*6777b538SAndroid Build Coastguard Worker }
416*6777b538SAndroid Build Coastguard Worker }
417*6777b538SAndroid Build Coastguard Worker return entries_.end();
418*6777b538SAndroid Build Coastguard Worker }
419*6777b538SAndroid Build Coastguard Worker
420*6777b538SAndroid Build Coastguard Worker // Linear scan through all entries to find least recently used entry (by oldest
421*6777b538SAndroid Build Coastguard Worker // |last_use_time_ticks_| and evict it from |entries_|.
EvictLeastRecentlyUsedEntry()422*6777b538SAndroid Build Coastguard Worker void HttpAuthCache::EvictLeastRecentlyUsedEntry() {
423*6777b538SAndroid Build Coastguard Worker DCHECK(entries_.size() == kMaxNumRealmEntries);
424*6777b538SAndroid Build Coastguard Worker base::TimeTicks now_ticks = tick_clock_->NowTicks();
425*6777b538SAndroid Build Coastguard Worker
426*6777b538SAndroid Build Coastguard Worker EntryMap::iterator oldest_entry_it = entries_.end();
427*6777b538SAndroid Build Coastguard Worker base::TimeTicks oldest_last_use_time_ticks = now_ticks;
428*6777b538SAndroid Build Coastguard Worker
429*6777b538SAndroid Build Coastguard Worker for (auto it = entries_.begin(); it != entries_.end(); ++it) {
430*6777b538SAndroid Build Coastguard Worker Entry& entry = it->second;
431*6777b538SAndroid Build Coastguard Worker if (entry.last_use_time_ticks_ < oldest_last_use_time_ticks ||
432*6777b538SAndroid Build Coastguard Worker oldest_entry_it == entries_.end()) {
433*6777b538SAndroid Build Coastguard Worker oldest_entry_it = it;
434*6777b538SAndroid Build Coastguard Worker oldest_last_use_time_ticks = entry.last_use_time_ticks_;
435*6777b538SAndroid Build Coastguard Worker }
436*6777b538SAndroid Build Coastguard Worker }
437*6777b538SAndroid Build Coastguard Worker DCHECK(oldest_entry_it != entries_.end());
438*6777b538SAndroid Build Coastguard Worker entries_.erase(oldest_entry_it);
439*6777b538SAndroid Build Coastguard Worker }
440*6777b538SAndroid Build Coastguard Worker
441*6777b538SAndroid Build Coastguard Worker } // namespace net
442