blob: 5b74f3b94763452761530052ebc31a31c8388ad3 [file] [log] [blame]
// Copyright 2017 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 "components/safe_browsing/browser/base_parallel_resource_throttle.h"
#include <utility>
#include "base/threading/thread_task_runner_handle.h"
#include "components/safe_browsing/browser/browser_url_loader_throttle.h"
#include "components/safe_browsing/browser/url_checker_delegate.h"
#include "content/public/browser/resource_request_info.h"
#include "net/http/http_request_headers.h"
#include "net/log/net_log_with_source.h"
#include "net/url_request/url_request.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/resource_response.h"
namespace safe_browsing {
class BaseParallelResourceThrottle::URLLoaderThrottleHolder
: public content::URLLoaderThrottle::Delegate {
public:
URLLoaderThrottleHolder(BaseParallelResourceThrottle* owner,
std::unique_ptr<BrowserURLLoaderThrottle> throttle)
: owner_(owner), throttle_(std::move(throttle)) {
throttle_->set_delegate(this);
}
~URLLoaderThrottleHolder() override = default;
BrowserURLLoaderThrottle* throttle() const { return throttle_.get(); }
uint32_t inside_delegate_calls() const { return inside_delegate_calls_; }
// content::URLLoaderThrottle::Delegate implementation:
void CancelWithError(int error_code,
base::StringPiece custom_reason) override {
if (!owner_)
return;
ScopedDelegateCall scoped_delegate_call(this);
owner_->MayDeferCancelResourceLoad();
}
void Resume() override {
if (!owner_)
return;
ScopedDelegateCall scoped_delegate_call(this);
owner_->ResumeResourceLoad();
}
void Detach() {
owner_ = nullptr;
}
private:
class ScopedDelegateCall {
public:
explicit ScopedDelegateCall(URLLoaderThrottleHolder* holder)
: holder_(holder) {
holder_->inside_delegate_calls_++;
}
~ScopedDelegateCall() { holder_->inside_delegate_calls_--; }
private:
URLLoaderThrottleHolder* const holder_;
DISALLOW_COPY_AND_ASSIGN(ScopedDelegateCall);
};
BaseParallelResourceThrottle* owner_;
uint32_t inside_delegate_calls_ = 0;
std::unique_ptr<BrowserURLLoaderThrottle> throttle_;
DISALLOW_COPY_AND_ASSIGN(URLLoaderThrottleHolder);
};
BaseParallelResourceThrottle::BaseParallelResourceThrottle(
const net::URLRequest* request,
content::ResourceType resource_type,
scoped_refptr<UrlCheckerDelegate> url_checker_delegate)
: request_(request), resource_type_(resource_type) {
const content::ResourceRequestInfo* info =
content::ResourceRequestInfo::ForRequest(request_);
auto throttle = BrowserURLLoaderThrottle::MaybeCreate(
std::move(url_checker_delegate), info->GetWebContentsGetterForRequest());
url_loader_throttle_holder_ =
std::make_unique<URLLoaderThrottleHolder>(this, std::move(throttle));
}
BaseParallelResourceThrottle::~BaseParallelResourceThrottle() {
if (url_loader_throttle_holder_->inside_delegate_calls() > 0) {
// The BrowserURLLoaderThrottle owned by |url_loader_throttle_holder_| is
// calling into this object. In this case, delay destruction of
// |url_loader_throttle_holder_|, so that the BrowserURLLoaderThrottle
// doesn't need to worry about any delegate calls may destroy it
// synchronously.
url_loader_throttle_holder_->Detach();
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
FROM_HERE, std::move(url_loader_throttle_holder_));
}
}
void BaseParallelResourceThrottle::WillStartRequest(bool* defer) {
throttle_in_band_ = true;
if (should_cancel_on_notification_) {
CancelResourceLoad();
return;
}
network::ResourceRequest resource_request;
net::HttpRequestHeaders full_headers;
resource_request.headers = request_->GetFullRequestHeaders(&full_headers)
? full_headers
: request_->extra_request_headers();
resource_request.load_flags = request_->load_flags();
resource_request.resource_type = resource_type_;
const content::ResourceRequestInfo* info =
content::ResourceRequestInfo::ForRequest(request_);
resource_request.has_user_gesture = info && info->HasUserGesture();
resource_request.url = request_->url();
resource_request.method = request_->method();
url_loader_throttle_holder_->throttle()->WillStartRequest(&resource_request,
defer);
DCHECK(!*defer);
throttle_in_band_ = false;
}
void BaseParallelResourceThrottle::WillRedirectRequest(
const net::RedirectInfo& redirect_info,
bool* defer) {
throttle_in_band_ = true;
if (should_cancel_on_notification_) {
CancelResourceLoad();
return;
}
// The safe browsing URLLoaderThrottle doesn't use the |resource_head|,
// |to_be_modified_headers|, |modified_headers| or |new_url| parameters, so
// pass in an empty struct to avoid changing ResourceThrottle signature.
network::ResourceResponseHead resource_head;
std::vector<std::string> to_be_removed_headers;
net::HttpRequestHeaders modified_headers;
net::RedirectInfo redirect_info_copy = redirect_info;
url_loader_throttle_holder_->throttle()->WillRedirectRequest(
&redirect_info_copy, resource_head, defer, &to_be_removed_headers,
&modified_headers);
DCHECK(!*defer);
throttle_in_band_ = false;
}
void BaseParallelResourceThrottle::WillProcessResponse(bool* defer) {
throttle_in_band_ = true;
if (should_cancel_on_notification_) {
CancelResourceLoad();
return;
}
network::ResourceResponseHead response_head;
url_loader_throttle_holder_->throttle()->WillProcessResponse(
GURL(), &response_head, defer);
if (!*defer)
throttle_in_band_ = false;
}
const char* BaseParallelResourceThrottle::GetNameForLogging() const {
return "BaseParallelResourceThrottle";
}
bool BaseParallelResourceThrottle::MustProcessResponseBeforeReadingBody() {
// The response body should not be cached before SafeBrowsing confirms that it
// is safe to do so.
return true;
}
void BaseParallelResourceThrottle::CancelResourceLoad() {
DCHECK(throttle_in_band_);
throttle_in_band_ = false;
Cancel();
}
void BaseParallelResourceThrottle::ResumeResourceLoad() {
DCHECK(throttle_in_band_);
throttle_in_band_ = false;
Resume();
}
void BaseParallelResourceThrottle::MayDeferCancelResourceLoad() {
if (!throttle_in_band_)
should_cancel_on_notification_ = true;
else
CancelResourceLoad();
}
} // namespace safe_browsing