blob: 93d85f73b6301d71490b3d45716704a4089ae16b [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/renderer_host/data_reduction_proxy_resource_throttle_android.h"
#include "base/logging.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/prerender/prerender_contents.h"
#include "chrome/browser/profiles/profile_io_data.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/resource_context.h"
#include "content/public/browser/resource_controller.h"
#include "content/public/browser/resource_request_info.h"
#include "content/public/browser/web_contents.h"
#include "net/base/load_flags.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/redirect_info.h"
#include "net/url_request/url_request.h"
using content::BrowserThread;
using content::ResourceThrottle;
// TODO(eroman): Downgrade these CHECK()s to DCHECKs once there is more
// unit test coverage.
// TODO(sgurun) following the comment above, also provide tests for
// checking whether the headers are injected correctly and the SPDY proxy
// origin is tested properly.
const char* DataReductionProxyResourceThrottle::kUnsafeUrlProceedHeader =
"X-Unsafe-Url-Proceed";
// static
DataReductionProxyResourceThrottle*
DataReductionProxyResourceThrottle::MaybeCreate(
net::URLRequest* request,
content::ResourceContext* resource_context,
content::ResourceType resource_type,
SafeBrowsingService* sb_service) {
ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context);
// Don't create the throttle if we can't handle the request.
if (io_data->IsOffTheRecord() || !io_data->IsDataReductionProxyEnabled() ||
request->url().SchemeIsCryptographic()) {
return NULL;
}
return new DataReductionProxyResourceThrottle(request, resource_type,
sb_service);
}
DataReductionProxyResourceThrottle::DataReductionProxyResourceThrottle(
net::URLRequest* request,
content::ResourceType resource_type,
SafeBrowsingService* safe_browsing)
: state_(STATE_NONE),
safe_browsing_(safe_browsing),
request_(request),
is_subresource_(resource_type != content::RESOURCE_TYPE_MAIN_FRAME),
is_subframe_(resource_type == content::RESOURCE_TYPE_SUB_FRAME) {
}
DataReductionProxyResourceThrottle::~DataReductionProxyResourceThrottle() { }
void DataReductionProxyResourceThrottle::WillRedirectRequest(
const net::RedirectInfo& redirect_info,
bool* defer) {
CHECK(state_ == STATE_NONE);
// Save the redirect urls for possible malware detail reporting later.
redirect_urls_.push_back(redirect_info.new_url);
// We need to check the new URL before following the redirect.
SBThreatType threat_type = CheckUrl();
if (threat_type == SB_THREAT_TYPE_SAFE)
return;
if (request_->load_flags() & net::LOAD_PREFETCH) {
controller()->Cancel();
return;
}
const content::ResourceRequestInfo* info =
content::ResourceRequestInfo::ForRequest(request_);
state_ = STATE_DISPLAYING_BLOCKING_PAGE;
SafeBrowsingUIManager::UnsafeResource unsafe_resource;
unsafe_resource.url = redirect_info.new_url;
unsafe_resource.original_url = request_->original_url();
unsafe_resource.redirect_urls = redirect_urls_;
unsafe_resource.is_subresource = is_subresource_;
unsafe_resource.is_subframe = is_subframe_;
unsafe_resource.threat_type = threat_type;
unsafe_resource.callback = base::Bind(
&DataReductionProxyResourceThrottle::OnBlockingPageComplete, AsWeakPtr());
unsafe_resource.render_process_host_id = info->GetChildID();
unsafe_resource.render_view_id = info->GetRouteID();
unsafe_resource.threat_source = SafeBrowsingUIManager::FROM_DATA_SAVER;
*defer = true;
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
base::Bind(
&DataReductionProxyResourceThrottle::StartDisplayingBlockingPage,
AsWeakPtr(), safe_browsing_->ui_manager(), unsafe_resource));
}
const char* DataReductionProxyResourceThrottle::GetNameForLogging() const {
return "DataReductionProxyResourceThrottle";
}
// static
void DataReductionProxyResourceThrottle::StartDisplayingBlockingPage(
const base::WeakPtr<DataReductionProxyResourceThrottle>& throttle,
scoped_refptr<SafeBrowsingUIManager> ui_manager,
const SafeBrowsingUIManager::UnsafeResource& resource) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
content::RenderViewHost* rvh = content::RenderViewHost::FromID(
resource.render_process_host_id, resource.render_view_id);
if (rvh) {
content::WebContents* web_contents =
content::WebContents::FromRenderViewHost(rvh);
prerender::PrerenderContents* prerender_contents =
prerender::PrerenderContents::FromWebContents(web_contents);
if (prerender_contents) {
prerender_contents->Destroy(prerender::FINAL_STATUS_SAFE_BROWSING);
content::BrowserThread::PostTask(
content::BrowserThread::IO,
FROM_HERE,
base::Bind(resource.callback, false));
return;
}
}
ui_manager->DisplayBlockingPage(resource);
}
// SafeBrowsingService::UrlCheckCallback implementation, called on the IO
// thread when the user has decided to proceed with the current request, or
// go back.
void DataReductionProxyResourceThrottle::OnBlockingPageComplete(bool proceed) {
CHECK(state_ == STATE_DISPLAYING_BLOCKING_PAGE);
state_ = STATE_NONE;
if (proceed)
ResumeRequest();
else
controller()->Cancel();
}
SBThreatType DataReductionProxyResourceThrottle::CheckUrl() {
SBThreatType result = SB_THREAT_TYPE_SAFE;
// TODO(sgurun) Check for spdy proxy origin.
if (request_->response_headers() == NULL)
return result;
if (request_->response_headers()->HasHeader("X-Phishing-Url"))
result = SB_THREAT_TYPE_URL_PHISHING;
else if (request_->response_headers()->HasHeader("X-Malware-Url"))
result = SB_THREAT_TYPE_URL_MALWARE;
// If safe browsing is disabled and the request is sent to the DRP server,
// we need to break the redirect loop by setting the extra header.
if (result != SB_THREAT_TYPE_SAFE && !safe_browsing_->enabled()) {
request_->SetExtraRequestHeaderByName(kUnsafeUrlProceedHeader, "1", true);
result = SB_THREAT_TYPE_SAFE;
}
return result;
}
void DataReductionProxyResourceThrottle::ResumeRequest() {
CHECK(state_ == STATE_NONE);
// Inject the header before resuming the request.
request_->SetExtraRequestHeaderByName(kUnsafeUrlProceedHeader, "1", true);
controller()->Resume();
}