safe_browsing_resource_handler.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2011 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_handler.h"
6
7#include "base/logging.h"
8#include "chrome/browser/renderer_host/global_request_id.h"
9#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
10#include "chrome/browser/renderer_host/resource_message_filter.h"
11#include "chrome/common/resource_response.h"
12#include "net/base/net_errors.h"
13#include "net/base/io_buffer.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
23SafeBrowsingResourceHandler::SafeBrowsingResourceHandler(
24    ResourceHandler* handler,
25    int render_process_host_id,
26    int render_view_id,
27    ResourceType::Type resource_type,
28    SafeBrowsingService* safe_browsing,
29    ResourceDispatcherHost* resource_dispatcher_host)
30    : state_(STATE_NONE),
31      defer_state_(DEFERRED_NONE),
32      safe_browsing_result_(SafeBrowsingService::URL_SAFE),
33      deferred_request_id_(-1),
34      next_handler_(handler),
35      render_process_host_id_(render_process_host_id),
36      render_view_id_(render_view_id),
37      safe_browsing_(safe_browsing),
38      rdh_(resource_dispatcher_host),
39      resource_type_(resource_type) {
40}
41
42SafeBrowsingResourceHandler::~SafeBrowsingResourceHandler() {
43}
44
45bool SafeBrowsingResourceHandler::OnUploadProgress(int request_id,
46                                                   uint64 position,
47                                                   uint64 size) {
48  return next_handler_->OnUploadProgress(request_id, position, size);
49}
50
51bool SafeBrowsingResourceHandler::OnRequestRedirected(
52    int request_id,
53    const GURL& new_url,
54    ResourceResponse* response,
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 next_handler_->OnRequestRedirected(
65        request_id, new_url, response, defer);
66  }
67
68  // If the URL couldn't be verified synchronously, defer following the
69  // redirect until the SafeBrowsing check is complete. Store the redirect
70  // context so we can pass it on to other handlers once we have completed
71  // our check.
72  defer_state_ = DEFERRED_REDIRECT;
73  deferred_request_id_ = request_id;
74  deferred_url_ = new_url;
75  deferred_redirect_response_ = response;
76  *defer = true;
77
78  return true;
79}
80
81bool SafeBrowsingResourceHandler::OnResponseStarted(
82    int request_id, ResourceResponse* response) {
83  CHECK(state_ == STATE_NONE);
84  CHECK(defer_state_ == DEFERRED_NONE);
85  return next_handler_->OnResponseStarted(request_id, response);
86}
87
88void SafeBrowsingResourceHandler::OnCheckUrlTimeout() {
89  CHECK(state_ == STATE_CHECKING_URL);
90  CHECK(defer_state_ != DEFERRED_NONE);
91  safe_browsing_->CancelCheck(this);
92  OnBrowseUrlCheckResult(deferred_url_, SafeBrowsingService::URL_SAFE);
93}
94
95bool SafeBrowsingResourceHandler::OnWillStart(int request_id,
96                                              const GURL& url,
97                                              bool* defer) {
98  // We need to check the new URL before starting the request.
99  if (CheckUrl(url))
100    return next_handler_->OnWillStart(request_id, url, defer);
101
102  // If the URL couldn't be verified synchronously, defer starting the
103  // request until the check has completed.
104  defer_state_ = DEFERRED_START;
105  deferred_request_id_ = request_id;
106  deferred_url_ = url;
107  *defer = true;
108
109  return true;
110}
111
112bool SafeBrowsingResourceHandler::OnWillRead(int request_id,
113                                             net::IOBuffer** buf, int* buf_size,
114                                             int min_size) {
115  CHECK(state_ == STATE_NONE);
116  CHECK(defer_state_ == DEFERRED_NONE);
117  return next_handler_->OnWillRead(request_id, buf, buf_size, min_size);
118}
119
120bool SafeBrowsingResourceHandler::OnReadCompleted(int request_id,
121                                                  int* bytes_read) {
122  CHECK(state_ == STATE_NONE);
123  CHECK(defer_state_ == DEFERRED_NONE);
124  return next_handler_->OnReadCompleted(request_id, bytes_read);
125}
126
127bool SafeBrowsingResourceHandler::OnResponseCompleted(
128    int request_id, const net::URLRequestStatus& status,
129    const std::string& security_info) {
130  Shutdown();
131  return next_handler_->OnResponseCompleted(request_id, status, security_info);
132}
133
134void SafeBrowsingResourceHandler::OnRequestClosed() {
135  Shutdown();
136  next_handler_->OnRequestClosed();
137}
138
139// SafeBrowsingService::Client implementation, called on the IO thread once
140// the URL has been classified.
141void SafeBrowsingResourceHandler::OnBrowseUrlCheckResult(
142    const GURL& url, SafeBrowsingService::UrlCheckResult result) {
143  CHECK(state_ == STATE_CHECKING_URL);
144  CHECK(defer_state_ != DEFERRED_NONE);
145  CHECK(url == deferred_url_) << "Was expecting: " << deferred_url_
146                              << " but got: " << url;
147
148  timer_.Stop();  // Cancel the timeout timer.
149  safe_browsing_result_ = result;
150  state_ = STATE_NONE;
151
152  if (result == SafeBrowsingService::URL_SAFE) {
153    // Log how much time the safe browsing check cost us.
154    base::TimeDelta pause_delta;
155    pause_delta = base::TimeTicks::Now() - url_check_start_time_;
156    safe_browsing_->LogPauseDelay(pause_delta);
157
158    // Continue the request.
159    ResumeRequest();
160  } else {
161    StartDisplayingBlockingPage(url, result);
162  }
163
164  Release();  // Balances the AddRef() in CheckingUrl().
165}
166
167void SafeBrowsingResourceHandler::StartDisplayingBlockingPage(
168    const GURL& url,
169    SafeBrowsingService::UrlCheckResult result) {
170  CHECK(state_ == STATE_NONE);
171  CHECK(defer_state_ != DEFERRED_NONE);
172  CHECK(deferred_request_id_ != -1);
173
174  state_ = STATE_DISPLAYING_BLOCKING_PAGE;
175  AddRef();  // Balanced in OnBlockingPageComplete().
176
177  // Grab the original url of this request as well.
178  GURL original_url;
179  net::URLRequest* request = rdh_->GetURLRequest(
180      GlobalRequestID(render_process_host_id_, deferred_request_id_));
181  if (request)
182    original_url = request->original_url();
183  else
184    original_url = url;
185
186  safe_browsing_->DisplayBlockingPage(
187      url, original_url, redirect_urls_, resource_type_,
188      result, this, render_process_host_id_, render_view_id_);
189}
190
191// SafeBrowsingService::Client implementation, called on the IO thread when
192// the user has decided to proceed with the current request, or go back.
193void SafeBrowsingResourceHandler::OnBlockingPageComplete(bool proceed) {
194  CHECK(state_ == STATE_DISPLAYING_BLOCKING_PAGE);
195  state_ = STATE_NONE;
196
197  if (proceed) {
198    safe_browsing_result_ = SafeBrowsingService::URL_SAFE;
199    ResumeRequest();
200  } else {
201    rdh_->CancelRequest(render_process_host_id_, deferred_request_id_, false);
202  }
203
204  Release();  // Balances the AddRef() in StartDisplayingBlockingPage().
205}
206
207void SafeBrowsingResourceHandler::Shutdown() {
208  if (state_ == STATE_CHECKING_URL) {
209    timer_.Stop();
210    safe_browsing_->CancelCheck(this);
211    state_ = STATE_NONE;
212    // Balance the AddRef() from CheckUrl() which would ordinarily be
213    // balanced by OnUrlCheckResult().
214    Release();
215  }
216}
217
218bool SafeBrowsingResourceHandler::CheckUrl(const GURL& url) {
219  CHECK(state_ == STATE_NONE);
220  bool succeeded_synchronously = safe_browsing_->CheckBrowseUrl(url, this);
221  if (succeeded_synchronously) {
222    safe_browsing_result_ = SafeBrowsingService::URL_SAFE;
223    safe_browsing_->LogPauseDelay(base::TimeDelta());  // No delay.
224    return true;
225  }
226
227  AddRef();  // Balanced in OnUrlCheckResult().
228  state_ = STATE_CHECKING_URL;
229
230  // Record the start time of the check.
231  url_check_start_time_ = base::TimeTicks::Now();
232
233  // Start a timer to abort the check if it takes too long.
234  timer_.Start(base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs),
235               this, &SafeBrowsingResourceHandler::OnCheckUrlTimeout);
236
237  return false;
238}
239
240void SafeBrowsingResourceHandler::ResumeRequest() {
241  CHECK(state_ == STATE_NONE);
242  CHECK(defer_state_ != DEFERRED_NONE);
243
244  // Resume whatever stage got paused by the safe browsing check.
245  switch (defer_state_) {
246    case DEFERRED_START:
247      ResumeStart();
248      break;
249    case DEFERRED_REDIRECT:
250      ResumeRedirect();
251      break;
252    case DEFERRED_NONE:
253      NOTREACHED();
254      break;
255  }
256}
257
258void SafeBrowsingResourceHandler::ResumeStart() {
259  CHECK(defer_state_ == DEFERRED_START);
260  CHECK(deferred_request_id_ != -1);
261  defer_state_ = DEFERRED_NONE;
262
263  // Retrieve the details for the paused OnWillStart().
264  int request_id = deferred_request_id_;
265  GURL url = deferred_url_;
266
267  ClearDeferredRequestInfo();
268
269  // Give the other resource handlers a chance to defer starting.
270  bool defer = false;
271  // TODO(eroman): the return value is being lost here. Should
272  // use it to cancel the request.
273  next_handler_->OnWillStart(request_id, url, &defer);
274  if (!defer)
275    rdh_->StartDeferredRequest(render_process_host_id_, request_id);
276}
277
278void SafeBrowsingResourceHandler::ResumeRedirect() {
279  CHECK(defer_state_ == DEFERRED_REDIRECT);
280  defer_state_ = DEFERRED_NONE;
281
282  // Retrieve the details for the paused OnReceivedRedirect().
283  int request_id = deferred_request_id_;
284  GURL redirect_url = deferred_url_;
285  scoped_refptr<ResourceResponse> redirect_response =
286      deferred_redirect_response_;
287
288  ClearDeferredRequestInfo();
289
290  // Give the other resource handlers a chance to handle the redirect.
291  bool defer = false;
292  // TODO(eroman): the return value is being lost here. Should
293  // use it to cancel the request.
294  next_handler_->OnRequestRedirected(request_id, redirect_url,
295                                     redirect_response, &defer);
296  if (!defer) {
297    rdh_->FollowDeferredRedirect(render_process_host_id_, request_id,
298                                 false, GURL());
299  }
300}
301
302void SafeBrowsingResourceHandler::ClearDeferredRequestInfo() {
303  deferred_request_id_ = -1;
304  deferred_url_ = GURL();
305  deferred_redirect_response_ = NULL;
306}
307