blob: 8f6c4b177b91d5857f6d1c607e71f70a482808fe [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/renderer/websocket_sb_handshake_throttle.h"
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/stringprintf.h"
#include "content/public/common/resource_type.h"
#include "content/public/renderer/render_frame.h"
#include "ipc/ipc_message.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "net/http/http_request_headers.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_url.h"
namespace safe_browsing {
WebSocketSBHandshakeThrottle::WebSocketSBHandshakeThrottle(
mojom::SafeBrowsing* safe_browsing,
int render_frame_id)
: render_frame_id_(render_frame_id),
callbacks_(nullptr),
safe_browsing_(safe_browsing),
result_(Result::UNKNOWN),
weak_factory_(this) {}
WebSocketSBHandshakeThrottle::~WebSocketSBHandshakeThrottle() {
// ThrottleHandshake() should always be called, but since that is done all the
// way over in Blink, just avoid logging if it is not called rather than
// DCHECK()ing.
if (start_time_.is_null())
return;
if (result_ == Result::UNKNOWN) {
result_ = Result::ABANDONED;
UMA_HISTOGRAM_TIMES("SafeBrowsing.WebSocket.Elapsed.Abandoned",
base::TimeTicks::Now() - start_time_);
}
UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.WebSocket.Result", result_,
Result::RESULT_COUNT);
}
void WebSocketSBHandshakeThrottle::ThrottleHandshake(
const blink::WebURL& url,
blink::WebCallbacks<void, const blink::WebString&>* callbacks) {
DCHECK(!callbacks_);
DCHECK(!url_checker_);
callbacks_ = callbacks;
url_ = url;
int load_flags = 0;
start_time_ = base::TimeTicks::Now();
safe_browsing_->CreateCheckerAndCheck(
render_frame_id_, mojo::MakeRequest(&url_checker_), url, "GET",
net::HttpRequestHeaders(), load_flags,
content::RESOURCE_TYPE_SUB_RESOURCE, false /* has_user_gesture */,
false /* originated_from_service_worker */,
base::BindOnce(&WebSocketSBHandshakeThrottle::OnCheckResult,
weak_factory_.GetWeakPtr()));
// This use of base::Unretained() is safe because the handler will not be
// called after |url_checker_| is destroyed, and it is owned by this object.
url_checker_.set_connection_error_handler(
base::BindOnce(&WebSocketSBHandshakeThrottle::OnConnectionError,
base::Unretained(this)));
}
void WebSocketSBHandshakeThrottle::OnCompleteCheck(bool proceed,
bool showed_interstitial) {
DCHECK(!start_time_.is_null());
base::TimeDelta elapsed = base::TimeTicks::Now() - start_time_;
if (proceed) {
result_ = Result::SAFE;
UMA_HISTOGRAM_TIMES("SafeBrowsing.WebSocket.Elapsed.Safe", elapsed);
callbacks_->OnSuccess();
} else {
// When the insterstitial is dismissed the page is navigated and this object
// is destroyed before reaching here.
result_ = Result::BLOCKED;
UMA_HISTOGRAM_TIMES("SafeBrowsing.WebSocket.Elapsed.Blocked", elapsed);
callbacks_->OnError(blink::WebString::FromUTF8(base::StringPrintf(
"WebSocket connection to %s failed safe browsing check",
url_.spec().c_str())));
}
// |this| is destroyed here.
}
void WebSocketSBHandshakeThrottle::OnCheckResult(
mojom::UrlCheckNotifierRequest slow_check_notifier,
bool proceed,
bool showed_interstitial) {
if (!slow_check_notifier.is_pending()) {
OnCompleteCheck(proceed, showed_interstitial);
return;
}
// TODO(yzshen): Notify the network service to pause processing response body.
if (!notifier_binding_) {
notifier_binding_ =
std::make_unique<mojo::Binding<mojom::UrlCheckNotifier>>(this);
}
notifier_binding_->Bind(std::move(slow_check_notifier));
}
void WebSocketSBHandshakeThrottle::OnConnectionError() {
DCHECK_EQ(result_, Result::UNKNOWN);
url_checker_.reset();
notifier_binding_.reset();
// Make the destructor record NOT_SUPPORTED in the result histogram.
result_ = Result::NOT_SUPPORTED;
// Don't record the time elapsed because it's unlikely to be meaningful.
callbacks_->OnSuccess();
// |this| is destroyed here.
}
} // namespace safe_browsing