Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(98)

Side by Side Diff: components/safe_browsing_db/database_manager.cc

Issue 2233103002: Move full hash caching logic to v4_get_hash_protocol_manager (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Bring back the histogram to check if there were any hits in the response from the server Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/safe_browsing_db/database_manager.h" 5 #include "components/safe_browsing_db/database_manager.h"
6 6
7 #include "base/metrics/histogram_macros.h" 7 #include "base/metrics/histogram_macros.h"
8 #include "components/safe_browsing_db/v4_get_hash_protocol_manager.h" 8 #include "components/safe_browsing_db/v4_get_hash_protocol_manager.h"
9 #include "content/public/browser/browser_thread.h" 9 #include "content/public/browser/browser_thread.h"
10 #include "net/url_request/url_request_context_getter.h" 10 #include "net/url_request/url_request_context_getter.h"
11 #include "url/gurl.h" 11 #include "url/gurl.h"
12 12
13 using content::BrowserThread; 13 using content::BrowserThread;
14 14
15 namespace {
16
17 // Enumerate full hash cache hits/misses for histogramming purposes.
18 // DO NOT CHANGE THE ORDERING OF THESE VALUES.
19 enum V4FullHashCacheResultType {
20 // Full hashes for which there is no cache hit.
21 FULL_HASH_CACHE_MISS = 0,
22
23 // Full hashes with a cache hit.
24 FULL_HASH_CACHE_HIT = 1,
25
26 // Full hashes with a negative cache hit.
27 FULL_HASH_NEGATIVE_CACHE_HIT = 2,
28
29 // Memory space for histograms is determined by the max. ALWAYS
30 // ADD NEW VALUES BEFORE THIS ONE.
31 FULL_HASH_CACHE_RESULT_MAX
32 };
33
34 // Enumerate GetHash hits/misses for histogramming purposes. DO NOT CHANGE THE
35 // ORDERING OF THESE VALUES.
36 enum V4GetHashCheckResultType {
37 // Successful responses which returned no full hashes.
38 GET_HASH_CHECK_EMPTY = 0,
39
40 // Successful responses for which one or more of the full hashes matched.
41 GET_HASH_CHECK_HIT = 1,
42
43 // Successful responses which weren't empty but have no matches.
44 GET_HASH_CHECK_MISS = 2,
45
46 // Memory space for histograms is determined by the max. ALWAYS
47 // ADD NEW VALUES BEFORE THIS ONE.
48 GET_HASH_CHECK_RESULT_MAX
49 };
50
51 // Record a full hash cache hit result.
52 void RecordV4FullHashCacheResult(
53 V4FullHashCacheResultType result_type) {
54 UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4FullHashCacheResult", result_type,
55 FULL_HASH_CACHE_RESULT_MAX);
56 }
57
58 // Record a GetHash hit result.
59 void RecordV4GetHashCheckResult(
60 V4GetHashCheckResultType result_type) {
61 UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4GetHashCheckResult", result_type,
62 GET_HASH_CHECK_RESULT_MAX);
63 }
64
65 } // namespace
66
67 namespace safe_browsing { 15 namespace safe_browsing {
68 16
69 SafeBrowsingDatabaseManager::SafeBrowsingDatabaseManager() 17 SafeBrowsingDatabaseManager::SafeBrowsingDatabaseManager() {}
70 : v4_get_hash_protocol_manager_(NULL) {
71 }
72 18
73 SafeBrowsingDatabaseManager::~SafeBrowsingDatabaseManager() { 19 SafeBrowsingDatabaseManager::~SafeBrowsingDatabaseManager() {
74 DCHECK(v4_get_hash_protocol_manager_ == NULL); 20 DCHECK(!v4_get_hash_protocol_manager_);
75 } 21 }
76 22
77 void SafeBrowsingDatabaseManager::StartOnIOThread( 23 void SafeBrowsingDatabaseManager::StartOnIOThread(
78 net::URLRequestContextGetter* request_context_getter, 24 net::URLRequestContextGetter* request_context_getter,
79 const V4ProtocolConfig& config) { 25 const V4ProtocolConfig& config) {
80 DCHECK_CURRENTLY_ON(BrowserThread::IO); 26 DCHECK_CURRENTLY_ON(BrowserThread::IO);
81 27
28 base::hash_set<UpdateListIdentifier> stores_to_look({GetChromeUrlApiId()});
82 v4_get_hash_protocol_manager_ = V4GetHashProtocolManager::Create( 29 v4_get_hash_protocol_manager_ = V4GetHashProtocolManager::Create(
83 request_context_getter, config); 30 request_context_getter, stores_to_look, config);
84 } 31 }
85 32
86 // |shutdown| not used. Destroys the v4 protocol managers. This may be called 33 // |shutdown| not used. Destroys the v4 protocol managers. This may be called
87 // multiple times during the life of the DatabaseManager. 34 // multiple times during the life of the DatabaseManager.
88 // Must be called on IO thread. 35 // Must be called on IO thread.
89 void SafeBrowsingDatabaseManager::StopOnIOThread(bool shutdown) { 36 void SafeBrowsingDatabaseManager::StopOnIOThread(bool shutdown) {
90 DCHECK_CURRENTLY_ON(BrowserThread::IO); 37 DCHECK_CURRENTLY_ON(BrowserThread::IO);
91 // This cancels all in-flight GetHash requests.
92 if (v4_get_hash_protocol_manager_) {
93 delete v4_get_hash_protocol_manager_;
94 v4_get_hash_protocol_manager_ = NULL;
95 }
96 38
97 // Delete pending checks, calling back any clients with empty metadata. 39 // Delete pending checks, calling back any clients with empty metadata.
98 for (auto* check : api_checks_) { 40 for (const SafeBrowsingApiCheck* check : api_checks_) {
99 if (check->client()) { 41 if (check->client()) {
100 check->client()-> 42 check->client()->
101 OnCheckApiBlacklistUrlResult(check->url(), ThreatMetadata()); 43 OnCheckApiBlacklistUrlResult(check->url(), ThreatMetadata());
102 } 44 }
103 } 45 }
104 base::STLDeleteElements(&api_checks_); 46
47 // This cancels all in-flight GetHash requests.
48 v4_get_hash_protocol_manager_.reset();
105 } 49 }
106 50
107 SafeBrowsingDatabaseManager::ApiCheckSet::iterator 51 SafeBrowsingDatabaseManager::ApiCheckSet::iterator
108 SafeBrowsingDatabaseManager::FindClientApiCheck(Client* client) { 52 SafeBrowsingDatabaseManager::FindClientApiCheck(Client* client) {
109 DCHECK_CURRENTLY_ON(BrowserThread::IO); 53 DCHECK_CURRENTLY_ON(BrowserThread::IO);
110 for (ApiCheckSet::iterator it = api_checks_.begin(); 54 for (ApiCheckSet::iterator it = api_checks_.begin();
111 it != api_checks_.end(); ++it) { 55 it != api_checks_.end(); ++it) {
112 if ((*it)->client() == client) { 56 if ((*it)->client() == client) {
113 return it; 57 return it;
114 } 58 }
115 } 59 }
116 return api_checks_.end(); 60 return api_checks_.end();
117 } 61 }
118 62
119 bool SafeBrowsingDatabaseManager::CancelApiCheck(Client* client) { 63 bool SafeBrowsingDatabaseManager::CancelApiCheck(Client* client) {
120 DCHECK_CURRENTLY_ON(BrowserThread::IO); 64 DCHECK_CURRENTLY_ON(BrowserThread::IO);
121 ApiCheckSet::iterator it = FindClientApiCheck(client); 65 ApiCheckSet::iterator it = FindClientApiCheck(client);
122 if (it != api_checks_.end()) { 66 if (it != api_checks_.end()) {
123 delete *it;
124 api_checks_.erase(it); 67 api_checks_.erase(it);
125 return true; 68 return true;
126 } 69 }
127 NOTREACHED(); 70 NOTREACHED();
128 return false; 71 return false;
129 } 72 }
130 73
131 bool SafeBrowsingDatabaseManager::CheckApiBlacklistUrl(const GURL& url, 74 bool SafeBrowsingDatabaseManager::CheckApiBlacklistUrl(const GURL& url,
132 Client* client) { 75 Client* client) {
133 DCHECK_CURRENTLY_ON(BrowserThread::IO); 76 DCHECK_CURRENTLY_ON(BrowserThread::IO);
134 DCHECK(v4_get_hash_protocol_manager_); 77 DCHECK(v4_get_hash_protocol_manager_);
135 78
136 // Make sure we can check this url. 79 // Make sure we can check this url.
137 if (!(url.SchemeIs(url::kHttpScheme) || url.SchemeIs(url::kHttpsScheme))) { 80 if (!(url.SchemeIs(url::kHttpScheme) || url.SchemeIs(url::kHttpsScheme))) {
138 return true; 81 return true;
139 } 82 }
140 83
141 // There can only be one in-progress check for the same client at a time. 84 // There can only be one in-progress check for the same client at a time.
142 DCHECK(FindClientApiCheck(client) == api_checks_.end()); 85 DCHECK(FindClientApiCheck(client) == api_checks_.end());
143 86
144 // Compute a list of hashes for this url. 87 std::unique_ptr<SafeBrowsingApiCheck> check(
145 std::vector<SBFullHash> full_hashes; 88 new SafeBrowsingApiCheck(url, client));
146 UrlToFullHashes(url, false, &full_hashes); 89 api_checks_.insert(check.get());
147 if (full_hashes.empty()) 90 v4_get_hash_protocol_manager_->GetFullHashesWithApis(
148 return true; 91 url, base::Bind(&SafeBrowsingDatabaseManager::OnThreatMetadataResponse,
149 92 base::Unretained(this), base::Passed(std::move(check))));
150 // First check the cache.
151
152 // Used to determine cache expiration.
153 base::Time now = base::Time::Now();
154
155 std::vector<SBFullHashResult> cached_results;
156 std::vector<SBPrefix> prefixes_needing_reqs;
157 GetFullHashCachedResults(SB_THREAT_TYPE_API_ABUSE,
158 full_hashes, now, &prefixes_needing_reqs, &cached_results);
159
160 if (prefixes_needing_reqs.empty() && cached_results.empty())
161 return true;
162
163 SafeBrowsingApiCheck* check =
164 new SafeBrowsingApiCheck(url, prefixes_needing_reqs, full_hashes,
165 cached_results, client);
166 api_checks_.insert(check);
167
168 if (prefixes_needing_reqs.empty()) {
169 check->set_start_time(base::TimeTicks::Now());
170 // We can call the callback immediately if no prefixes require a request.
171 // The |full_hash_results| representing the results fromt eh SB server will
172 // be empty.
173 std::vector<SBFullHashResult> full_hash_results;
174 HandleGetHashesWithApisResults(check, full_hash_results, base::Time());
175 return false;
176 }
177
178 v4_get_hash_protocol_manager_->GetFullHashesWithApis(prefixes_needing_reqs,
179 base::Bind(&SafeBrowsingDatabaseManager::HandleGetHashesWithApisResults,
180 base::Unretained(this), check));
181 93
182 return false; 94 return false;
183 } 95 }
184 96
185 void SafeBrowsingDatabaseManager::GetFullHashCachedResults( 97 void SafeBrowsingDatabaseManager::OnThreatMetadataResponse(
186 const SBThreatType& threat_type, 98 std::unique_ptr<SafeBrowsingApiCheck> check,
187 const std::vector<SBFullHash>& full_hashes, 99 const ThreatMetadata& md) {
188 base::Time now,
189 std::vector<SBPrefix>* prefixes_needing_reqs,
190 std::vector<SBFullHashResult>* cached_results) {
191 DCHECK(prefixes_needing_reqs);
192 prefixes_needing_reqs->clear();
193 DCHECK(cached_results);
194 cached_results->clear();
195
196 // Caching behavior is documented here:
197 // https://developers.google.com/safe-browsing/v4/caching#about-caching
198 //
199 // The cache operates as follows:
200 // Lookup:
201 // Case 1: The prefix is in the cache.
202 // Case a: The full hash is in the cache.
203 // Case i : The positive full hash result has not expired.
204 // The result is unsafe and we do not need to send a new
205 // request.
206 // Case ii: The positive full hash result has expired.
207 // We need to send a request for full hashes.
208 // Case b: The full hash is not in the cache.
209 // Case i : The negative cache entry has not expired.
210 // The result is still safe and we do not need to send a
211 // new request.
212 // Case ii: The negative cache entry has expired.
213 // We need to send a request for full hashes.
214 // Case 2: The prefix is not in the cache.
215 // We need to send a request for full hashes.
216 //
217 // Eviction:
218 // SBCachedFullHashResult entries can be removed from the cache only when
219 // the negative cache expire time and the cache expire time of all full
220 // hash results for that prefix have expired.
221 // Individual full hash results can be removed from the prefix's
222 // cache entry if they expire AND their expire time is after the negative
223 // cache expire time.
224 for (const SBFullHash& full_hash : full_hashes) {
225 auto entry = v4_full_hash_cache_[threat_type].find(full_hash.prefix);
226 if (entry != v4_full_hash_cache_[threat_type].end()) {
227 // Case 1.
228 SBCachedFullHashResult& cache_result = entry->second;
229
230 const SBFullHashResult* found_full_hash = nullptr;
231 size_t matched_idx = 0;
232 for (const SBFullHashResult& hash_result : cache_result.full_hashes) {
233 if (SBFullHashEqual(full_hash, hash_result.hash)) {
234 found_full_hash = &hash_result;
235 break;
236 }
237 ++matched_idx;
238 }
239
240 if (found_full_hash) {
241 // Case a.
242 if (found_full_hash->cache_expire_after > now) {
243 // Case i.
244 cached_results->push_back(*found_full_hash);
245 RecordV4FullHashCacheResult(FULL_HASH_CACHE_HIT);
246 } else {
247 // Case ii.
248 prefixes_needing_reqs->push_back(full_hash.prefix);
249 RecordV4FullHashCacheResult(FULL_HASH_CACHE_MISS);
250 // If the negative cache expire time has passed, evict this full hash
251 // result from the cache.
252 if (cache_result.expire_after <= now) {
253 cache_result.full_hashes.erase(
254 cache_result.full_hashes.begin() + matched_idx);
255 // If there are no more full hashes, we can evict the entire entry.
256 if (cache_result.full_hashes.empty()) {
257 v4_full_hash_cache_[threat_type].erase(entry);
258 }
259 }
260 }
261 } else {
262 // Case b.
263 if (cache_result.expire_after > now) {
264 // Case i.
265 RecordV4FullHashCacheResult(FULL_HASH_NEGATIVE_CACHE_HIT);
266 } else {
267 // Case ii.
268 prefixes_needing_reqs->push_back(full_hash.prefix);
269 RecordV4FullHashCacheResult(FULL_HASH_CACHE_MISS);
270 }
271 }
272 } else {
273 // Case 2.
274 prefixes_needing_reqs->push_back(full_hash.prefix);
275 RecordV4FullHashCacheResult(FULL_HASH_CACHE_MISS);
276 }
277 }
278
279 // Multiple full hashes could share a prefix, remove duplicates.
280 // TODO(kcarattini): Make |prefixes_needing_reqs| a set.
281 std::sort(prefixes_needing_reqs->begin(), prefixes_needing_reqs->end());
282 prefixes_needing_reqs->erase(std::unique(prefixes_needing_reqs->begin(),
283 prefixes_needing_reqs->end()), prefixes_needing_reqs->end());
284 }
285
286 void SafeBrowsingDatabaseManager::HandleGetHashesWithApisResults(
287 SafeBrowsingApiCheck* check,
288 const std::vector<SBFullHashResult>& full_hash_results,
289 const base::Time& negative_cache_expire) {
290 DCHECK_CURRENTLY_ON(BrowserThread::IO); 100 DCHECK_CURRENTLY_ON(BrowserThread::IO);
291 DCHECK(check); 101 DCHECK(check);
292 102
293 // Record the network time.
294 if (!check->start_time().is_null()) {
295 UMA_HISTOGRAM_LONG_TIMES("SafeBrowsing.GetV4HashNetwork",
296 base::TimeTicks::Now() - check->start_time());
297 }
298
299 // If the time is uninitialized, don't cache the results.
300 if (!negative_cache_expire.is_null()) {
301 // Cache the results.
302 // Create or reset all cached results for this prefix.
303 for (const SBPrefix& prefix : check->prefixes()) {
304 v4_full_hash_cache_[SB_THREAT_TYPE_API_ABUSE][prefix] =
305 SBCachedFullHashResult(negative_cache_expire);
306 }
307 // Insert any full hash hits. Note that there may be one, multiple, or no
308 // full hashes for any given prefix.
309 for (const SBFullHashResult& result : full_hash_results) {
310 v4_full_hash_cache_[SB_THREAT_TYPE_API_ABUSE][result.hash.prefix].
311 full_hashes.push_back(result);
312 }
313 }
314
315 // If the check is not in |api_checks_| then the request was cancelled by the 103 // If the check is not in |api_checks_| then the request was cancelled by the
316 // client. 104 // client.
317 ApiCheckSet::iterator it = api_checks_.find(check); 105 ApiCheckSet::iterator it = api_checks_.find(check.get());
318 if (it == api_checks_.end()) 106 if (it == api_checks_.end())
319 return; 107 return;
320 108
321 ThreatMetadata md;
322 // Merge the metadata from all matching results.
323 // Note: A full hash may have a result in both the cached results (from
324 // its own cache lookup) and in the server results (if another full hash
325 // with the same prefix needed to request results from the server). In this
326 // unlikely case, the two results' metadata will be merged.
327 bool get_hash_hit =
328 PopulateApiMetadataResult(full_hash_results, check->full_hashes(), &md);
329 PopulateApiMetadataResult(check->cached_results(), check->full_hashes(), &md);
330
331 if (get_hash_hit) {
332 RecordV4GetHashCheckResult(GET_HASH_CHECK_HIT);
333 } else if (full_hash_results.empty()) {
334 RecordV4GetHashCheckResult(GET_HASH_CHECK_EMPTY);
335 } else {
336 RecordV4GetHashCheckResult(GET_HASH_CHECK_MISS);
337 }
338
339 check->client()->OnCheckApiBlacklistUrlResult(check->url(), md); 109 check->client()->OnCheckApiBlacklistUrlResult(check->url(), md);
340 api_checks_.erase(it); 110 api_checks_.erase(it);
341 delete check;
342 }
343
344 // TODO(kcarattini): This is O(N^2). Look at improving performance by
345 // using a map, sorting or doing binary search etc..
346 bool SafeBrowsingDatabaseManager::PopulateApiMetadataResult(
347 const std::vector<SBFullHashResult>& results,
348 const std::vector<SBFullHash>& full_hashes,
349 ThreatMetadata* md) {
350 DCHECK(md);
351 bool hit = false;
352 for (const SBFullHashResult& result : results) {
353 for (const SBFullHash& full_hash : full_hashes) {
354 if (SBFullHashEqual(full_hash, result.hash)) {
355 md->api_permissions.insert(result.metadata.api_permissions.begin(),
356 result.metadata.api_permissions.end());
357 hit = true;
358 break;
359 }
360 }
361 }
362 return hit;
363 } 111 }
364 112
365 SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::SafeBrowsingApiCheck( 113 SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::SafeBrowsingApiCheck(
366 const GURL& url, 114 const GURL& url,
367 const std::vector<SBPrefix>& prefixes,
368 const std::vector<SBFullHash>& full_hashes,
369 const std::vector<SBFullHashResult>& cached_results,
370 Client* client) 115 Client* client)
371 : url_(url), prefixes_(prefixes), full_hashes_(full_hashes), 116 : url_(url), client_(client) {}
372 cached_results_(cached_results), client_(client) {
373 }
374 117
375 SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::~SafeBrowsingApiCheck() { 118 SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::~SafeBrowsingApiCheck() {}
376 }
377 119
378 } // namespace safe_browsing 120 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698