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.
5 #include "chrome/browser/renderer_host/safe_browsing_resource_throttle.h"
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"
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.
20 static const int kCheckUrlTimeoutMs = 5000;
22 // TODO(eroman): Downgrade these CHECK()s to DCHECKs once there is more
23 // unit test coverage.
25 SafeBrowsingResourceThrottle::SafeBrowsingResourceThrottle(
26 const net::URLRequest* request,
28 SafeBrowsingService* safe_browsing)
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()),
35 is_subresource_(is_subresource) {
38 SafeBrowsingResourceThrottle::~SafeBrowsingResourceThrottle() {
39 if (state_ == STATE_CHECKING_URL)
40 database_manager_->CancelCheck(this);
43 void SafeBrowsingResourceThrottle::WillStartRequest(bool* defer) {
44 // We need to check the new URL before starting the request.
45 if (CheckUrl(request_->url()))
48 // If the URL couldn't be verified synchronously, defer starting the
49 // request until the check has completed.
50 defer_state_ = DEFERRED_START;
54 void SafeBrowsingResourceThrottle::WillRedirectRequest(const GURL& new_url,
56 CHECK(state_ == STATE_NONE);
57 CHECK(defer_state_ == DEFERRED_NONE);
59 // Save the redirect urls for possible malware detail reporting later.
60 redirect_urls_.push_back(new_url);
62 // We need to check the new URL before following the redirect.
63 if (CheckUrl(new_url))
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
70 defer_state_ = DEFERRED_REDIRECT;
74 // SafeBrowsingService::Client implementation, called on the IO thread once
75 // the URL has been classified.
76 void 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;
83 timer_.Stop(); // Cancel the timeout timer.
84 threat_type_ = threat_type;
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_);
91 // Continue the request.
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
98 controller()->Cancel();
99 should_show_blocking_page = false;
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->
108 if (prerender_tracker->TryCancelOnIOThread(
111 prerender::FINAL_STATUS_SAFE_BROWSING)) {
112 controller()->Cancel();
113 should_show_blocking_page = false;
117 if (should_show_blocking_page)
118 StartDisplayingBlockingPage(url, threat_type);
122 void SafeBrowsingResourceThrottle::StartDisplayingBlockingPage(
123 const GURL& url, SBThreatType threat_type) {
124 CHECK(state_ == STATE_NONE);
125 CHECK(defer_state_ != DEFERRED_NONE);
127 state_ = STATE_DISPLAYING_BLOCKING_PAGE;
129 const content::ResourceRequestInfo* info =
130 content::ResourceRequestInfo::ForRequest(request_);
131 ui_manager_->DisplayBlockingPage(
133 request_->original_url(),
138 &SafeBrowsingResourceThrottle::OnBlockingPageComplete, AsWeakPtr()),
143 // SafeBrowsingService::UrlCheckCallback implementation, called on the IO
144 // thread when the user has decided to proceed with the current request, or
146 void SafeBrowsingResourceThrottle::OnBlockingPageComplete(bool proceed) {
147 CHECK(state_ == STATE_DISPLAYING_BLOCKING_PAGE);
151 threat_type_ = SB_THREAT_TYPE_SAFE;
154 controller()->Cancel();
158 bool 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.
167 state_ = STATE_CHECKING_URL;
168 url_being_checked_ = url;
170 // Record the start time of the check.
171 url_check_start_time_ = base::TimeTicks::Now();
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);
181 void SafeBrowsingResourceThrottle::OnCheckUrlTimeout() {
182 CHECK(state_ == STATE_CHECKING_URL);
183 CHECK(defer_state_ != DEFERRED_NONE);
185 database_manager_->CancelCheck(this);
186 OnCheckBrowseUrlResult(url_being_checked_, SB_THREAT_TYPE_SAFE);
189 void SafeBrowsingResourceThrottle::ResumeRequest() {
190 CHECK(state_ == STATE_NONE);
191 CHECK(defer_state_ != DEFERRED_NONE);
193 defer_state_ = DEFERRED_NONE;
194 controller()->Resume();