blob: bd2e5b496b8ade249986664364b169912ba29ce9 [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/logging.h"
#include "content/browser/loader/navigation_url_loader_impl_core.h"
#include "content/browser/loader/netlog_observer.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/common/security_style_util.h"
#include "content/public/browser/navigation_data.h"
#include "content/public/browser/resource_controller.h"
#include "content/public/browser/resource_dispatcher_host_delegate.h"
#include "content/public/browser/ssl_status.h"
#include "content/public/browser/stream_handle.h"
#include "content/public/common/resource_response.h"
#include "net/base/net_errors.h"
#include "net/url_request/url_request.h"
namespace content {
void NavigationResourceHandler::GetSSLStatusForRequest(
const GURL& url,
const net::SSLInfo& ssl_info,
int child_id,
SSLStatus* ssl_status) {
DCHECK(ssl_info.cert);
*ssl_status = SSLStatus(GetSecurityStyleForResource(
url, !!ssl_info.cert, ssl_info.cert_status),
ssl_info.cert, ssl_info);
}
NavigationResourceHandler::NavigationResourceHandler(
net::URLRequest* request,
NavigationURLLoaderImplCore* core,
ResourceDispatcherHostDelegate* resource_dispatcher_host_delegate)
: ResourceHandler(request),
core_(core),
resource_dispatcher_host_delegate_(resource_dispatcher_host_delegate) {
core_->set_resource_handler(this);
writer_.set_immediate_mode(true);
}
NavigationResourceHandler::~NavigationResourceHandler() {
if (core_) {
core_->NotifyRequestFailed(false, net::ERR_ABORTED);
DetachFromCore();
}
}
void NavigationResourceHandler::Cancel() {
controller()->Cancel();
core_ = nullptr;
}
void NavigationResourceHandler::FollowRedirect() {
controller()->Resume();
}
void NavigationResourceHandler::ProceedWithResponse() {
// Detach from the loader; at this point, the request is now owned by the
// StreamHandle sent in OnResponseStarted.
DetachFromCore();
controller()->Resume();
}
void NavigationResourceHandler::SetController(ResourceController* controller) {
writer_.set_controller(controller);
ResourceHandler::SetController(controller);
}
bool NavigationResourceHandler::OnRequestRedirected(
const net::RedirectInfo& redirect_info,
ResourceResponse* response,
bool* defer) {
DCHECK(core_);
// TODO(davidben): Perform a CSP check here, and anything else that would have
// been done renderer-side.
NetLogObserver::PopulateResponseInfo(request(), response);
core_->NotifyRequestRedirected(redirect_info, response);
*defer = true;
return true;
}
bool NavigationResourceHandler::OnResponseStarted(ResourceResponse* response,
bool* defer) {
DCHECK(core_);
ResourceRequestInfoImpl* info = GetRequestInfo();
// If the MimeTypeResourceHandler intercepted this request and converted it
// into a download, it will still call OnResponseStarted and immediately
// cancel. Ignore the call; OnReadCompleted will happen shortly.
//
// TODO(davidben): Move the dispatch out of MimeTypeResourceHandler. Perhaps
// all the way to the UI thread. Downloads, user certificates, etc., should be
// dispatched at the navigation layer.
if (info->IsDownload() || info->is_stream())
return true;
StreamContext* stream_context =
GetStreamContextForResourceContext(info->GetContext());
writer_.InitializeStream(stream_context->registry(),
request()->url().GetOrigin());
NetLogObserver::PopulateResponseInfo(request(), response);
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();
}
SSLStatus ssl_status;
if (request()->ssl_info().cert.get()) {
GetSSLStatusForRequest(request()->url(), request()->ssl_info(),
info->GetChildID(), &ssl_status);
}
core_->NotifyResponseStarted(response, writer_.stream()->CreateHandle(),
ssl_status, std::move(cloned_data));
*defer = true;
return true;
}
bool NavigationResourceHandler::OnWillStart(const GURL& url, bool* defer) {
return true;
}
bool NavigationResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
int* buf_size,
int min_size) {
writer_.OnWillRead(buf, buf_size, min_size);
return true;
}
bool NavigationResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
writer_.OnReadCompleted(bytes_read, defer);
return true;
}
void NavigationResourceHandler::OnResponseCompleted(
const net::URLRequestStatus& status,
bool* defer) {
// If the request has already committed, close the stream and leave it as-is.
//
// TODO(davidben): The net error code should be passed through StreamWriter
// down to the stream's consumer. See https://crbug.com/426162.
if (writer_.stream()) {
writer_.Finalize();
return;
}
if (core_) {
DCHECK_NE(net::OK, status.error());
core_->NotifyRequestFailed(request()->response_info().was_cached,
status.error());
DetachFromCore();
}
}
void NavigationResourceHandler::OnDataDownloaded(int bytes_downloaded) {
NOTREACHED();
}
void NavigationResourceHandler::DetachFromCore() {
DCHECK(core_);
core_->set_resource_handler(nullptr);
core_ = nullptr;
}
} // namespace content