blob: f4bb551151beceee4914d7c8350398389a6b0bed [file] [log] [blame]
// Copyright 2014 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 "content/browser/loader/navigation_resource_handler.h"
#include <memory>
#include "base/bind.h"
#include "base/logging.h"
#include "base/optional.h"
#include "content/browser/loader/navigation_metrics.h"
#include "content/browser/loader/navigation_url_loader_impl_core.h"
#include "content/browser/loader/resource_controller.h"
#include "content/browser/loader/resource_loader.h"
#include "content/browser/loader/resource_request_info_impl.h"
#include "content/browser/resource_context_impl.h"
#include "content/browser/streams/stream.h"
#include "content/browser/streams/stream_context.h"
#include "content/public/browser/navigation_data.h"
#include "content/public/browser/resource_dispatcher_host_delegate.h"
#include "content/public/browser/stream_handle.h"
#include "net/base/net_errors.h"
#include "net/http/transport_security_state.h"
#include "net/ssl/ssl_info.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
#include "services/network/public/cpp/resource_response.h"
#include "url/gurl.h"
namespace content {
NavigationResourceHandler::NavigationResourceHandler(
net::URLRequest* request,
std::unique_ptr<ResourceHandler> next_handler,
NavigationURLLoaderImplCore* core,
ResourceDispatcherHostDelegate* resource_dispatcher_host_delegate,
std::unique_ptr<StreamHandle> stream_handle)
: LayeredResourceHandler(request, std::move(next_handler)),
core_(core),
stream_handle_(std::move(stream_handle)),
resource_dispatcher_host_delegate_(resource_dispatcher_host_delegate) {
core_->set_resource_handler(this);
}
NavigationResourceHandler::~NavigationResourceHandler() {
if (core_) {
core_->NotifyRequestFailed(false, net::ERR_ABORTED, base::nullopt);
DetachFromCore();
}
}
void NavigationResourceHandler::Cancel() {
if (core_) {
DetachFromCore();
if (has_controller()) {
LayeredResourceHandler::Cancel();
} else {
OutOfBandCancel(net::ERR_ABORTED, true /* tell_renderer */);
}
}
}
void NavigationResourceHandler::FollowRedirect() {
DCHECK(response_);
DCHECK(redirect_info_);
DCHECK(has_controller());
next_handler_->OnRequestRedirected(*redirect_info_, response_.get(),
ReleaseController());
response_ = nullptr;
redirect_info_ = nullptr;
}
void NavigationResourceHandler::ProceedWithResponse() {
DCHECK(response_);
DCHECK(has_controller());
time_proceed_with_response_ = base::TimeTicks::Now();
// Detach from the loader; at this point, the request is now owned by the
// StreamHandle sent in OnResponseStarted.
DetachFromCore();
next_handler_->OnResponseStarted(response_.get(), ReleaseController());
response_ = nullptr;
}
void NavigationResourceHandler::OnRequestRedirected(
const net::RedirectInfo& redirect_info,
network::ResourceResponse* response,
std::unique_ptr<ResourceController> controller) {
DCHECK(!has_controller());
// The UI thread already cancelled the navigation. Do not proceed.
if (!core_) {
controller->Cancel();
return;
}
response->head.encoded_data_length = request()->GetTotalReceivedBytes();
core_->NotifyRequestRedirected(redirect_info, response);
HoldController(std::move(controller));
response_ = response;
redirect_info_ = std::make_unique<net::RedirectInfo>(redirect_info);
}
void NavigationResourceHandler::OnResponseStarted(
network::ResourceResponse* response,
std::unique_ptr<ResourceController> controller) {
DCHECK(!has_controller());
time_response_started_ = base::TimeTicks::Now();
// The UI thread already cancelled the navigation. Do not proceed.
if (!core_) {
controller->Cancel();
return;
}
ResourceRequestInfoImpl* info = GetRequestInfo();
response->head.encoded_data_length = request()->raw_header_size();
std::unique_ptr<NavigationData> cloned_data;
if (resource_dispatcher_host_delegate_) {
// Ask the embedder for a NavigationData instance.
NavigationData* navigation_data =
resource_dispatcher_host_delegate_->GetNavigationData(request());
// Clone the embedder's NavigationData before moving it to the UI thread.
if (navigation_data)
cloned_data = navigation_data->Clone();
}
core_->NotifyResponseStarted(response, std::move(stream_handle_),
request()->ssl_info(), std::move(cloned_data),
info->GetGlobalRequestID(), info->IsDownload(),
info->is_stream());
HoldController(std::move(controller));
response_ = response;
}
void NavigationResourceHandler::OnReadCompleted(
int bytes_read,
std::unique_ptr<ResourceController> controller) {
if (!has_completed_one_read_) {
has_completed_one_read_ = true;
base::TimeTicks time_first_read_completed = base::TimeTicks::Now();
RecordNavigationResourceHandlerMetrics(time_response_started_,
time_proceed_with_response_,
time_first_read_completed);
}
next_handler_->OnReadCompleted(bytes_read, std::move(controller));
}
void NavigationResourceHandler::OnResponseCompleted(
const net::URLRequestStatus& status,
std::unique_ptr<ResourceController> controller) {
if (core_) {
int net_error = status.error();
DCHECK_NE(net::OK, net_error);
base::Optional<net::SSLInfo> ssl_info;
if (net::IsCertStatusError(request()->ssl_info().cert_status)) {
ssl_info = request()->ssl_info();
}
core_->NotifyRequestFailed(request()->response_info().was_cached, net_error,
ssl_info);
DetachFromCore();
}
next_handler_->OnResponseCompleted(status, std::move(controller));
}
void NavigationResourceHandler::DetachFromCore() {
DCHECK(core_);
core_->set_resource_handler(nullptr);
core_ = nullptr;
}
} // namespace content