safe_browsing_resource_throttle.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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. 4 5#include "chrome/browser/renderer_host/safe_browsing_resource_throttle.h" 6 7#include "base/logging.h" 8#include "chrome/browser/browser_process.h" 9#include "chrome/browser/prerender/prerender_tracker.h" 10#include "chrome/browser/renderer_host/chrome_url_request_user_data.h" 11#include "chrome/browser/safe_browsing/safe_browsing_service.h" 12#include "content/public/browser/resource_controller.h" 13#include "content/public/browser/resource_request_info.h" 14#include "net/base/load_flags.h" 15#include "net/url_request/url_request.h" 16 17// Maximum time in milliseconds to wait for the safe browsing service to 18// verify a URL. After this amount of time the outstanding check will be 19// aborted, and the URL will be treated as if it were safe. 20static const int kCheckUrlTimeoutMs = 5000; 21 22// TODO(eroman): Downgrade these CHECK()s to DCHECKs once there is more 23// unit test coverage. 24 25SafeBrowsingResourceThrottle::SafeBrowsingResourceThrottle( 26 const net::URLRequest* request, 27 bool is_subresource, 28 SafeBrowsingService* safe_browsing) 29 : state_(STATE_NONE), 30 defer_state_(DEFERRED_NONE), 31 threat_type_(SB_THREAT_TYPE_SAFE), 32 database_manager_(safe_browsing->database_manager()), 33 ui_manager_(safe_browsing->ui_manager()), 34 request_(request), 35 is_subresource_(is_subresource) { 36} 37 38SafeBrowsingResourceThrottle::~SafeBrowsingResourceThrottle() { 39 if (state_ == STATE_CHECKING_URL) 40 database_manager_->CancelCheck(this); 41} 42 43void SafeBrowsingResourceThrottle::WillStartRequest(bool* defer) { 44 // We need to check the new URL before starting the request. 45 if (CheckUrl(request_->url())) 46 return; 47 48 // If the URL couldn't be verified synchronously, defer starting the 49 // request until the check has completed. 50 defer_state_ = DEFERRED_START; 51 *defer = true; 52} 53 54void SafeBrowsingResourceThrottle::WillRedirectRequest(const GURL& new_url, 55 bool* defer) { 56 CHECK(state_ == STATE_NONE); 57 CHECK(defer_state_ == DEFERRED_NONE); 58 59 // Save the redirect urls for possible malware detail reporting later. 60 redirect_urls_.push_back(new_url); 61 62 // We need to check the new URL before following the redirect. 63 if (CheckUrl(new_url)) 64 return; 65 66 // If the URL couldn't be verified synchronously, defer following the 67 // redirect until the SafeBrowsing check is complete. Store the redirect 68 // context so we can pass it on to other handlers once we have completed 69 // our check. 70 defer_state_ = DEFERRED_REDIRECT; 71 *defer = true; 72} 73 74const char* SafeBrowsingResourceThrottle::GetNameForLogging() const { 75 return "SafeBrowsingResourceThrottle"; 76} 77 78// SafeBrowsingService::Client implementation, called on the IO thread once 79// the URL has been classified. 80void SafeBrowsingResourceThrottle::OnCheckBrowseUrlResult( 81 const GURL& url, SBThreatType threat_type) { 82 CHECK(state_ == STATE_CHECKING_URL); 83 CHECK(defer_state_ != DEFERRED_NONE); 84 CHECK(url == url_being_checked_) << "Was expecting: " << url_being_checked_ 85 << " but got: " << url; 86 87 timer_.Stop(); // Cancel the timeout timer. 88 threat_type_ = threat_type; 89 state_ = STATE_NONE; 90 91 if (threat_type == SB_THREAT_TYPE_SAFE) { 92 // Log how much time the safe browsing check cost us. 93 ui_manager_->LogPauseDelay(base::TimeTicks::Now() - url_check_start_time_); 94 95 // Continue the request. 96 ResumeRequest(); 97 } else { 98 bool should_show_blocking_page = true; 99 if (request_->load_flags() & net::LOAD_PREFETCH) { 100 // Don't prefetch resources that fail safe browsing, disallow 101 // them. 102 controller()->Cancel(); 103 should_show_blocking_page = false; 104 } else { 105 ChromeURLRequestUserData* user_data = 106 ChromeURLRequestUserData::Get(request_); 107 if (user_data && user_data->is_prerender()) { 108 const content::ResourceRequestInfo* info = 109 content::ResourceRequestInfo::ForRequest(request_); 110 prerender::PrerenderTracker* prerender_tracker = g_browser_process-> 111 prerender_tracker(); 112 if (prerender_tracker->TryCancelOnIOThread( 113 info->GetChildID(), 114 info->GetRouteID(), 115 prerender::FINAL_STATUS_SAFE_BROWSING)) { 116 controller()->Cancel(); 117 should_show_blocking_page = false; 118 } 119 } 120 } 121 if (should_show_blocking_page) 122 StartDisplayingBlockingPage(url, threat_type); 123 } 124} 125 126void SafeBrowsingResourceThrottle::StartDisplayingBlockingPage( 127 const GURL& url, SBThreatType threat_type) { 128 CHECK(state_ == STATE_NONE); 129 CHECK(defer_state_ != DEFERRED_NONE); 130 131 state_ = STATE_DISPLAYING_BLOCKING_PAGE; 132 133 const content::ResourceRequestInfo* info = 134 content::ResourceRequestInfo::ForRequest(request_); 135 ui_manager_->DisplayBlockingPage( 136 url, 137 request_->original_url(), 138 redirect_urls_, 139 is_subresource_, 140 threat_type, 141 base::Bind( 142 &SafeBrowsingResourceThrottle::OnBlockingPageComplete, AsWeakPtr()), 143 info->GetChildID(), 144 info->GetRouteID()); 145} 146 147// SafeBrowsingService::UrlCheckCallback implementation, called on the IO 148// thread when the user has decided to proceed with the current request, or 149// go back. 150void SafeBrowsingResourceThrottle::OnBlockingPageComplete(bool proceed) { 151 CHECK(state_ == STATE_DISPLAYING_BLOCKING_PAGE); 152 state_ = STATE_NONE; 153 154 if (proceed) { 155 threat_type_ = SB_THREAT_TYPE_SAFE; 156 ResumeRequest(); 157 } else { 158 controller()->Cancel(); 159 } 160} 161 162bool SafeBrowsingResourceThrottle::CheckUrl(const GURL& url) { 163 CHECK(state_ == STATE_NONE); 164 bool succeeded_synchronously = database_manager_->CheckBrowseUrl(url, this); 165 if (succeeded_synchronously) { 166 threat_type_ = SB_THREAT_TYPE_SAFE; 167 ui_manager_->LogPauseDelay(base::TimeDelta()); // No delay. 168 return true; 169 } 170 171 state_ = STATE_CHECKING_URL; 172 url_being_checked_ = url; 173 174 // Record the start time of the check. 175 url_check_start_time_ = base::TimeTicks::Now(); 176 177 // Start a timer to abort the check if it takes too long. 178 timer_.Start(FROM_HERE, 179 base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs), 180 this, &SafeBrowsingResourceThrottle::OnCheckUrlTimeout); 181 182 return false; 183} 184 185void SafeBrowsingResourceThrottle::OnCheckUrlTimeout() { 186 CHECK(state_ == STATE_CHECKING_URL); 187 CHECK(defer_state_ != DEFERRED_NONE); 188 189 database_manager_->CancelCheck(this); 190 OnCheckBrowseUrlResult(url_being_checked_, SB_THREAT_TYPE_SAFE); 191} 192 193void SafeBrowsingResourceThrottle::ResumeRequest() { 194 CHECK(state_ == STATE_NONE); 195 CHECK(defer_state_ != DEFERRED_NONE); 196 197 defer_state_ = DEFERRED_NONE; 198 controller()->Resume(); 199} 200