blob: c049eb093063f117d35ca8515acaa1048f49db7d [file] [log] [blame]
// Copyright 2019 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/previews/previews_lite_page_redirect_url_loader.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
#include "components/previews/core/previews_lite_page_redirect.h"
#include "content/public/common/previews_state.h"
#include "net/http/http_status_code.h"
#include "net/http/http_util.h"
#include "net/url_request/redirect_util.h"
#include "services/network/public/cpp/resource_request.h"
namespace previews {
namespace {
// Used for mojo pipe size. Same constant as navigation code.
constexpr size_t kRedirectDefaultAllocationSize = 512 * 1024;
} // namespace
PreviewsLitePageRedirectURLLoader::PreviewsLitePageRedirectURLLoader(
const network::ResourceRequest& tentative_resource_request,
HandleRequest callback)
: modified_resource_request_(tentative_resource_request),
callback_(std::move(callback)),
binding_(this),
weak_ptr_factory_(this) {}
PreviewsLitePageRedirectURLLoader::~PreviewsLitePageRedirectURLLoader() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void PreviewsLitePageRedirectURLLoader::StartRedirectToPreview(
const net::HttpRequestHeaders& chrome_proxy_headers,
const scoped_refptr<network::SharedURLLoaderFactory>&
network_loader_factory,
int frame_tree_node_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
GURL lite_page_url = PreviewsLitePageNavigationThrottle::GetPreviewsURLForURL(
modified_resource_request_.url);
CreateRedirectInformation(lite_page_url);
modified_resource_request_.headers.MergeFrom(chrome_proxy_headers);
serving_url_loader_ = std::make_unique<PreviewsLitePageServingURLLoader>(
base::BindOnce(&PreviewsLitePageRedirectURLLoader::OnResultDetermined,
weak_ptr_factory_.GetWeakPtr()));
// |serving_url_loader_| can be null after this call.
serving_url_loader_->StartNetworkRequest(
modified_resource_request_, network_loader_factory, frame_tree_node_id);
}
void PreviewsLitePageRedirectURLLoader::StartRedirectToOriginalURL(
const GURL& original_url) {
CreateRedirectInformation(original_url);
std::move(callback_).Run(
nullptr, base::BindOnce(&PreviewsLitePageRedirectURLLoader::
StartHandlingRedirectToModifiedRequest,
weak_ptr_factory_.GetWeakPtr()));
}
void PreviewsLitePageRedirectURLLoader::CreateRedirectInformation(
const GURL& redirect_url) {
bool insecure_scheme_was_upgraded = false;
bool copy_fragment = true;
redirect_info_ = net::RedirectInfo::ComputeRedirectInfo(
modified_resource_request_.method, modified_resource_request_.url,
modified_resource_request_.site_for_cookies,
modified_resource_request_.top_frame_origin,
net::URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT,
modified_resource_request_.referrer_policy,
modified_resource_request_.referrer.spec(), net::HTTP_TEMPORARY_REDIRECT,
redirect_url, base::nullopt, insecure_scheme_was_upgraded, copy_fragment);
bool should_clear_upload = false;
net::RedirectUtil::UpdateHttpRequest(
modified_resource_request_.url, modified_resource_request_.method,
redirect_info_, base::nullopt, base::nullopt,
&modified_resource_request_.headers, &should_clear_upload);
DCHECK(!should_clear_upload);
modified_resource_request_.url = redirect_info_.new_url;
modified_resource_request_.method = redirect_info_.new_method;
modified_resource_request_.site_for_cookies =
redirect_info_.new_site_for_cookies;
modified_resource_request_.top_frame_origin =
redirect_info_.new_top_frame_origin;
modified_resource_request_.referrer = GURL(redirect_info_.new_referrer);
modified_resource_request_.referrer_policy =
redirect_info_.new_referrer_policy;
}
void PreviewsLitePageRedirectURLLoader::OnResultDetermined(
ServingLoaderResult result,
base::Optional<net::RedirectInfo> redirect_info,
scoped_refptr<network::ResourceResponse> response) {
DCHECK(!redirect_info || result == ServingLoaderResult::kRedirect);
DCHECK(!response || result == ServingLoaderResult::kRedirect);
switch (result) {
case ServingLoaderResult::kSuccess:
OnLitePageSuccess();
return;
case ServingLoaderResult::kFallback:
OnLitePageFallback();
return;
case ServingLoaderResult::kRedirect:
OnLitePageRedirect(redirect_info.value(), response->head);
return;
}
NOTREACHED();
}
void PreviewsLitePageRedirectURLLoader::OnLitePageSuccess() {
std::move(callback_).Run(
std::move(serving_url_loader_),
base::BindOnce(&PreviewsLitePageRedirectURLLoader::
StartHandlingRedirectToModifiedRequest,
weak_ptr_factory_.GetWeakPtr()));
}
void PreviewsLitePageRedirectURLLoader::OnLitePageRedirect(
const net::RedirectInfo& redirect_info,
const network::ResourceResponseHead& response_head) {
redirect_info_ = redirect_info;
response_head_ = response_head;
std::move(callback_).Run(
nullptr,
base::BindOnce(&PreviewsLitePageRedirectURLLoader::StartHandlingRedirect,
weak_ptr_factory_.GetWeakPtr()));
}
void PreviewsLitePageRedirectURLLoader::OnLitePageFallback() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::move(callback_).Run(nullptr, {});
}
void PreviewsLitePageRedirectURLLoader::StartHandlingRedirectToModifiedRequest(
const network::ResourceRequest& resource_request,
network::mojom::URLLoaderRequest request,
network::mojom::URLLoaderClientPtr client) {
response_head_.request_start = base::TimeTicks::Now();
response_head_.response_start = response_head_.request_start;
std::string header_string = base::StringPrintf(
"HTTP/1.1 %i Temporary Redirect\n"
"Location: %s\n",
net::HTTP_TEMPORARY_REDIRECT,
modified_resource_request_.url.spec().c_str());
scoped_refptr<net::HttpResponseHeaders> fake_headers_for_redirect =
new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders(
header_string.c_str(), header_string.length()));
response_head_.headers = fake_headers_for_redirect;
response_head_.encoded_data_length = 0;
StartHandlingRedirect(resource_request, std::move(request),
std::move(client));
}
void PreviewsLitePageRedirectURLLoader::StartHandlingRedirect(
const network::ResourceRequest& /* resource_request */,
network::mojom::URLLoaderRequest request,
network::mojom::URLLoaderClientPtr client) {
DCHECK(!binding_.is_bound());
binding_.Bind(std::move(request));
binding_.set_connection_error_handler(
base::BindOnce(&PreviewsLitePageRedirectURLLoader::OnConnectionClosed,
weak_ptr_factory_.GetWeakPtr()));
client_ = std::move(client);
mojo::DataPipe pipe(kRedirectDefaultAllocationSize);
if (!pipe.consumer_handle.is_valid()) {
delete this;
return;
}
client_->OnReceiveRedirect(redirect_info_, response_head_);
}
void PreviewsLitePageRedirectURLLoader::FollowRedirect(
const std::vector<std::string>& removed_headers,
const net::HttpRequestHeaders& modified_headers,
const base::Optional<GURL>& new_url) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Content should not hang onto old URLLoaders for redirects.
NOTREACHED();
}
void PreviewsLitePageRedirectURLLoader::ProceedWithResponse() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// This class never provides a response past the headers.
NOTREACHED();
}
void PreviewsLitePageRedirectURLLoader::SetPriority(
net::RequestPriority priority,
int32_t intra_priority_value) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Pass through.
serving_url_loader_->SetPriority(priority, intra_priority_value);
}
void PreviewsLitePageRedirectURLLoader::PauseReadingBodyFromNet() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Pass through.
serving_url_loader_->PauseReadingBodyFromNet();
}
void PreviewsLitePageRedirectURLLoader::ResumeReadingBodyFromNet() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Pass through.
serving_url_loader_->ResumeReadingBodyFromNet();
}
void PreviewsLitePageRedirectURLLoader::OnConnectionClosed() {
// This happens when content cancels the navigation. Close the network request
// and client handle and destroy |this|.
binding_.Close();
client_.reset();
delete this;
}
} // namespace previews