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