| Index: chrome/browser/safe_browsing/client_side_detection_service.cc
|
| diff --git a/chrome/browser/safe_browsing/client_side_detection_service.cc b/chrome/browser/safe_browsing/client_side_detection_service.cc
|
| index 008a2d2de44c1f90cd4cdaba323824fc6d4d285b..4adda8a0c207bf8054bfcf2133ed998314d146e0 100644
|
| --- a/chrome/browser/safe_browsing/client_side_detection_service.cc
|
| +++ b/chrome/browser/safe_browsing/client_side_detection_service.cc
|
| @@ -26,7 +26,14 @@
|
|
|
| namespace safe_browsing {
|
|
|
| -const int ClientSideDetectionService::kMaxReportsPerDay = 3;
|
| +const int ClientSideDetectionService::kMaxReportsPerInterval = 3;
|
| +
|
| +const base::TimeDelta ClientSideDetectionService::kReportsInterval =
|
| + base::TimeDelta::FromDays(1);
|
| +const base::TimeDelta ClientSideDetectionService::kNegativeCacheInterval =
|
| + base::TimeDelta::FromDays(1);
|
| +const base::TimeDelta ClientSideDetectionService::kPositiveCacheInterval =
|
| + base::TimeDelta::FromMinutes(30);
|
|
|
| const char ClientSideDetectionService::kClientReportPhishingUrl[] =
|
| "https://sb-ssl.google.com/safebrowsing/clientreport/phishing";
|
| @@ -38,6 +45,10 @@ struct ClientSideDetectionService::ClientReportInfo {
|
| GURL phishing_url;
|
| };
|
|
|
| +ClientSideDetectionService::CacheState::CacheState(bool phish, base::Time time)
|
| + : is_phishing(phish),
|
| + timestamp(time) {}
|
| +
|
| ClientSideDetectionService::ClientSideDetectionService(
|
| const FilePath& model_path,
|
| URLRequestContextGetter* request_context_getter)
|
| @@ -226,9 +237,26 @@ void ClientSideDetectionService::StartClientReportPhishingRequest(
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| scoped_ptr<ClientReportPhishingRequestCallback> cb(callback);
|
|
|
| - if (GetNumReportsPerDay() > kMaxReportsPerDay) {
|
| - LOG(WARNING) << "Too many report phishing requests sent in the last day, "
|
| - << "not checking " << phishing_url;
|
| + bool is_phishing;
|
| + if (GetCachedResult(phishing_url, &is_phishing)) {
|
| + VLOG(1) << "Satisfying request for " << phishing_url << " from cache";
|
| + UMA_HISTOGRAM_COUNTS("SBClientPhishing.RequestSatisfiedFromCache", 1);
|
| + cb->Run(phishing_url, is_phishing);
|
| + return;
|
| + }
|
| +
|
| + // We limit the number of distinct pings to kMaxReports, but we don't count
|
| + // urls already in the cache against this number. We don't want to start
|
| + // classifying too many pages as phishing, but for those that we already
|
| + // think are phishing we want to give ourselves a chance to fix false
|
| + // positives.
|
| + if (cache_.find(phishing_url) != cache_.end()) {
|
| + VLOG(1) << "Refreshing cache for " << phishing_url;
|
| + UMA_HISTOGRAM_COUNTS("SBClientPhishing.CacheRefresh", 1);
|
| + } else if (GetNumReports() > kMaxReportsPerInterval) {
|
| + VLOG(1) << "Too many report phishing requests sent in the last "
|
| + << kReportsInterval.InHours() << " hours, not checking "
|
| + << phishing_url;
|
| UMA_HISTOGRAM_COUNTS("SBClientPhishing.RequestNotSent", 1);
|
| cb->Run(phishing_url, false);
|
| return;
|
| @@ -239,9 +267,8 @@ void ClientSideDetectionService::StartClientReportPhishingRequest(
|
| request.set_client_score(static_cast<float>(score));
|
| std::string request_data;
|
| if (!request.SerializeToString(&request_data)) {
|
| - // For consistency, we always call the callback asynchronously, rather than
|
| - // directly from this method.
|
| - LOG(ERROR) << "Unable to serialize the CSD request. Proto file changed?";
|
| + UMA_HISTOGRAM_COUNTS("SBClientPhishing.RequestNotSerialized", 1);
|
| + VLOG(1) << "Unable to serialize the CSD request. Proto file changed?";
|
| cb->Run(phishing_url, false);
|
| return;
|
| }
|
| @@ -306,8 +333,11 @@ void ClientSideDetectionService::HandlePhishingVerdict(
|
| const std::string& data) {
|
| ClientPhishingResponse response;
|
| scoped_ptr<ClientReportInfo> info(client_phishing_reports_[source]);
|
| - if (status.is_success() && RC_REQUEST_OK == response_code &&
|
| + if (status.is_success() && RC_REQUEST_OK == response_code &&
|
| response.ParseFromString(data)) {
|
| + // Cache response, possibly flushing an old one.
|
| + cache_[info->phishing_url] =
|
| + make_linked_ptr(new CacheState(response.phishy(), base::Time::Now()));
|
| info->callback->Run(info->phishing_url, response.phishy());
|
| } else {
|
| DLOG(ERROR) << "Unable to get the server verdict for URL: "
|
| @@ -318,11 +348,53 @@ void ClientSideDetectionService::HandlePhishingVerdict(
|
| delete source;
|
| }
|
|
|
| -int ClientSideDetectionService::GetNumReportsPerDay() {
|
| - base::Time cutoff = base::Time::Now() - base::TimeDelta::FromDays(1);
|
| +bool ClientSideDetectionService::GetCachedResult(const GURL& url,
|
| + bool* is_phishing) {
|
| + UpdateCache();
|
| +
|
| + PhishingCache::iterator it = cache_.find(url);
|
| + if (it == cache_.end()) {
|
| + return false;
|
| + }
|
| +
|
| + // We still need to check if the result is valid.
|
| + const CacheState& cache_state = *it->second;
|
| + if (cache_state.is_phishing ?
|
| + cache_state.timestamp > base::Time::Now() - kPositiveCacheInterval :
|
| + cache_state.timestamp > base::Time::Now() - kNegativeCacheInterval) {
|
| + *is_phishing = cache_state.is_phishing;
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +void ClientSideDetectionService::UpdateCache() {
|
| + // Since we limit the number of requests but allow pass-through for cache
|
| + // refreshes, we don't want to remove elements from the cache if they
|
| + // could be used for this purpose even if we will not use the entry to
|
| + // satisfy the request from the cache.
|
| + base::TimeDelta positive_cache_interval =
|
| + std::max(kPositiveCacheInterval, kReportsInterval);
|
| + base::TimeDelta negative_cache_interval =
|
| + std::max(kNegativeCacheInterval, kReportsInterval);
|
| +
|
| + // Remove elements from the cache that will no longer be used.
|
| + for (PhishingCache::iterator it = cache_.begin(); it != cache_.end();) {
|
| + const CacheState& cache_state = *it->second;
|
| + if (cache_state.is_phishing ?
|
| + cache_state.timestamp > base::Time::Now() - positive_cache_interval :
|
| + cache_state.timestamp > base::Time::Now() - negative_cache_interval) {
|
| + ++it;
|
| + } else {
|
| + cache_.erase(it++);
|
| + }
|
| + }
|
| +}
|
| +
|
| +int ClientSideDetectionService::GetNumReports() {
|
| + base::Time cutoff = base::Time::Now() - kReportsInterval;
|
|
|
| - // Erase elements older than a day because we will never care about them
|
| - // again.
|
| + // Erase items older than cutoff because we will never care about them again.
|
| while (!phishing_report_times_.empty() &&
|
| phishing_report_times_.front() < cutoff) {
|
| phishing_report_times_.pop();
|
|
|