safe_browsing_resource_throttle.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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 74// SafeBrowsingService::Client implementation, called on the IO thread once 75// the URL has been classified. 76void SafeBrowsingResourceThrottle::OnCheckBrowseUrlResult( 77 const GURL& url, SBThreatType threat_type) { 78 CHECK(state_ == STATE_CHECKING_URL); 79 CHECK(defer_state_ != DEFERRED_NONE); 80 CHECK(url == url_being_checked_) << "Was expecting: " << url_being_checked_ 81 << " but got: " << url; 82 83 timer_.Stop(); // Cancel the timeout timer. 84 threat_type_ = threat_type; 85 state_ = STATE_NONE; 86 87 if (threat_type == SB_THREAT_TYPE_SAFE) { 88 // Log how much time the safe browsing check cost us. 89 ui_manager_->LogPauseDelay(base::TimeTicks::Now() - url_check_start_time_); 90 91 // Continue the request. 92 ResumeRequest(); 93 } else { 94 bool should_show_blocking_page = true; 95 if (request_->load_flags() & net::LOAD_PREFETCH) { 96 // Don't prefetch resources that fail safe browsing, disallow 97 // them. 98 controller()->Cancel(); 99 should_show_blocking_page = false; 100 } else { 101 ChromeURLRequestUserData* user_data = 102 ChromeURLRequestUserData::Get(request_); 103 if (user_data && user_data->is_prerender()) { 104 const content::ResourceRequestInfo* info = 105 content::ResourceRequestInfo::ForRequest(request_); 106 prerender::PrerenderTracker* prerender_tracker = g_browser_process-> 107 prerender_tracker(); 108 if (prerender_tracker->TryCancelOnIOThread( 109 info->GetChildID(), 110 info->GetRouteID(), 111 prerender::FINAL_STATUS_SAFE_BROWSING)) { 112 controller()->Cancel(); 113 should_show_blocking_page = false; 114 } 115 } 116 } 117 if (should_show_blocking_page) 118 StartDisplayingBlockingPage(url, threat_type); 119 } 120} 121 122void SafeBrowsingResourceThrottle::StartDisplayingBlockingPage( 123 const GURL& url, SBThreatType threat_type) { 124 CHECK(state_ == STATE_NONE); 125 CHECK(defer_state_ != DEFERRED_NONE); 126 127 state_ = STATE_DISPLAYING_BLOCKING_PAGE; 128 129 const content::ResourceRequestInfo* info = 130 content::ResourceRequestInfo::ForRequest(request_); 131 ui_manager_->DisplayBlockingPage( 132 url, 133 request_->original_url(), 134 redirect_urls_, 135 is_subresource_, 136 threat_type, 137 base::Bind( 138 &SafeBrowsingResourceThrottle::OnBlockingPageComplete, AsWeakPtr()), 139 info->GetChildID(), 140 info->GetRouteID()); 141} 142 143// SafeBrowsingService::UrlCheckCallback implementation, called on the IO 144// thread when the user has decided to proceed with the current request, or 145// go back. 146void SafeBrowsingResourceThrottle::OnBlockingPageComplete(bool proceed) { 147 CHECK(state_ == STATE_DISPLAYING_BLOCKING_PAGE); 148 state_ = STATE_NONE; 149 150 if (proceed) { 151 threat_type_ = SB_THREAT_TYPE_SAFE; 152 ResumeRequest(); 153 } else { 154 controller()->Cancel(); 155 } 156} 157 158bool SafeBrowsingResourceThrottle::CheckUrl(const GURL& url) { 159 CHECK(state_ == STATE_NONE); 160 bool succeeded_synchronously = database_manager_->CheckBrowseUrl(url, this); 161 if (succeeded_synchronously) { 162 threat_type_ = SB_THREAT_TYPE_SAFE; 163 ui_manager_->LogPauseDelay(base::TimeDelta()); // No delay. 164 return true; 165 } 166 167 state_ = STATE_CHECKING_URL; 168 url_being_checked_ = url; 169 170 // Record the start time of the check. 171 url_check_start_time_ = base::TimeTicks::Now(); 172 173 // Start a timer to abort the check if it takes too long. 174 timer_.Start(FROM_HERE, 175 base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs), 176 this, &SafeBrowsingResourceThrottle::OnCheckUrlTimeout); 177 178 return false; 179} 180 181void SafeBrowsingResourceThrottle::OnCheckUrlTimeout() { 182 CHECK(state_ == STATE_CHECKING_URL); 183 CHECK(defer_state_ != DEFERRED_NONE); 184 185 database_manager_->CancelCheck(this); 186 OnCheckBrowseUrlResult(url_being_checked_, SB_THREAT_TYPE_SAFE); 187} 188 189void SafeBrowsingResourceThrottle::ResumeRequest() { 190 CHECK(state_ == STATE_NONE); 191 CHECK(defer_state_ != DEFERRED_NONE); 192 193 defer_state_ = DEFERRED_NONE; 194 controller()->Resume(); 195} 196