Skip to content

Commit

Permalink
[keepalive-migration] Introduce KeepAliveURLLoader and its factory …
Browse files Browse the repository at this point in the history
…service

- See the [design doc] for the whole picture.
- The loader currently only forwards requests to network service and forwards response back to renderer (if alive).
- The E2E usage can be found at child CL https://crrev.com/c/4297074.

[design doc]: https://docs.google.com/document/d/1ZzxMMBvpqn8VZBZKnb7Go8TWjnrGcXuLS_USwVVRUvY/edit#

Bug: 1356128
Change-Id: If17dc7bff56f359110c09cbf8292d592714d00ec
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4297494
Reviewed-by: Takashi Toyoshima <toyoshim@chromium.org>
Reviewed-by: Tsuyoshi Horo <horo@chromium.org>
Commit-Queue: Ming-Ying Chung <mych@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1117931}
  • Loading branch information
mingyc authored and Chromium LUCI CQ committed Mar 16, 2023
1 parent 78dd56e commit f0e05e3
Show file tree
Hide file tree
Showing 7 changed files with 1,256 additions and 0 deletions.
4 changes: 4 additions & 0 deletions content/browser/BUILD.gn
Expand Up @@ -1185,6 +1185,10 @@ source_set("browser") {
"loader/download_utils_impl.h",
"loader/file_url_loader_factory.cc",
"loader/file_url_loader_factory.h",
"loader/keep_alive_url_loader.cc",
"loader/keep_alive_url_loader.h",
"loader/keep_alive_url_loader_service.cc",
"loader/keep_alive_url_loader_service.h",
"loader/merkle_integrity_source_stream.cc",
"loader/merkle_integrity_source_stream.h",
"loader/navigation_early_hints_manager.cc",
Expand Down
251 changes: 251 additions & 0 deletions content/browser/loader/keep_alive_url_loader.cc
@@ -0,0 +1,251 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/loader/keep_alive_url_loader.h"

#include "base/functional/bind.h"
#include "base/trace_event/trace_event.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/load_flags.h"
#include "net/http/http_request_headers.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/mojom/early_hints.mojom.h"
#include "services/network/public/mojom/fetch_api.mojom-shared.h"
#include "third_party/blink/public/common/features.h"

namespace content {

KeepAliveURLLoader::KeepAliveURLLoader(
int32_t request_id,
uint32_t options,
const network::ResourceRequest& resource_request,
mojo::PendingRemote<network::mojom::URLLoaderClient> forwarding_client,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory,
base::PassKey<KeepAliveURLLoaderService>)
: request_id_(request_id),
forwarding_client_(std::move(forwarding_client)) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(network_loader_factory);
DCHECK(!resource_request.trusted_params);
TRACE_EVENT2("loading", "KeepAliveURLLoader::KeepAliveURLLoader",
"request_id", request_id_, "url", resource_request.url);
TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("loading", "KeepAliveURLLoader",
request_id_, "url", resource_request.url);

// Asks the network service to create a URL loader with passed in params.
network_loader_factory->CreateLoaderAndStart(
loader_.BindNewPipeAndPassReceiver(), request_id, options,
resource_request, loader_receiver_.BindNewPipeAndPassRemote(),
traffic_annotation);
loader_receiver_.set_disconnect_handler(base::BindOnce(
&KeepAliveURLLoader::OnNetworkConnectionError, base::Unretained(this)));
forwarding_client_.set_disconnect_handler(base::BindOnce(
&KeepAliveURLLoader::OnRendererConnectionError, base::Unretained(this)));
}

KeepAliveURLLoader::~KeepAliveURLLoader() {
TRACE_EVENT1("loading", "KeepAliveURLLoader::~KeepAliveURLLoader",
"request_id", request_id_);
TRACE_EVENT_NESTABLE_ASYNC_END0("loading", "KeepAliveURLLoader", request_id_);
}

void KeepAliveURLLoader::set_on_delete_callback(
OnDeleteCallback on_delete_callback) {
on_delete_callback_ = std::move(on_delete_callback);
}

void KeepAliveURLLoader::FollowRedirect(
const std::vector<std::string>& removed_headers,
const net::HttpRequestHeaders& modified_headers,
const net::HttpRequestHeaders& modified_cors_exempt_headers,
const absl::optional<GURL>& new_url) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(loader_);
TRACE_EVENT2("loading", "KeepAliveURLLoader::FollowRedirect", "request_id",
request_id_, "url", new_url);

// Forwards the action to `loader_` in the network service.
loader_->FollowRedirect(removed_headers, modified_headers,
modified_cors_exempt_headers, new_url);
}

void KeepAliveURLLoader::SetPriority(net::RequestPriority priority,
int intra_priority_value) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(loader_);
TRACE_EVENT1("loading", "KeepAliveURLLoader::SetPriority", "request_id",
request_id_);

// Forwards the action to `loader_` in the network service.
loader_->SetPriority(priority, intra_priority_value);
}

void KeepAliveURLLoader::PauseReadingBodyFromNet() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(loader_);
TRACE_EVENT1("loading", "KeepAliveURLLoader::FollowRedirect", "request_id",
request_id_);

// Forwards the action to `loader_` in the network service.
loader_->PauseReadingBodyFromNet();
}

void KeepAliveURLLoader::ResumeReadingBodyFromNet() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(loader_);
TRACE_EVENT1("loading", "KeepAliveURLLoader::ResumeReadingBodyFromNet",
"request_id", request_id_);

// Forwards the action to `loader_` in the network service.
loader_->ResumeReadingBodyFromNet();
}

void KeepAliveURLLoader::OnReceiveEarlyHints(
network::mojom::EarlyHintsPtr early_hints) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TRACE_EVENT1("loading", "KeepAliveURLLoader::OnReceiveEarlyHints",
"request_id", request_id_);

if (forwarding_client_.is_bound() && forwarding_client_.is_connected()) {
// The renderer is alive, forwards the action.
forwarding_client_->OnReceiveEarlyHints(std::move(early_hints));
return;
}

// TODO(crbug.com/1356128): Handle in browser process.
}

void KeepAliveURLLoader::OnReceiveResponse(
network::mojom::URLResponseHeadPtr response,
mojo::ScopedDataPipeConsumerHandle body,
absl::optional<mojo_base::BigBuffer> cached_metadata) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TRACE_EVENT1("loading", "KeepAliveURLLoader::OnReceiveResponse", "request_id",
request_id_);

has_received_response_ = true;
// TODO(crbug.com/1424731): The renderer might exit before `OnReceiveRedirect`
// or `OnReceiveResponse` is called, or during their execution. In such case,
// `forwarding_client_` can't finish response handling. Figure out a way to
// negotiate shutdown timing via RenderFrameHostImpl::OnUnloadAck() and
// invalidate `forwarding_client_`.
if (forwarding_client_.is_bound() && forwarding_client_.is_connected()) {
// The renderer is alive, forwards the action.
// The receiver may fail to finish reading `response`, so response caching
// is not guaranteed.
forwarding_client_->OnReceiveResponse(std::move(response), std::move(body),
std::move(cached_metadata));
// TODO(crbug.com/1422645): Ensure that attributionsrc response handling is
// migrated to browser process.
return;
}

// No need to wait for `OnComplete()`.
// This loader should be deleted immediately to avoid hanged requests taking
// up resources.
std::move(on_delete_callback_).Run();
}

void KeepAliveURLLoader::OnReceiveRedirect(
const net::RedirectInfo& redirect_info,
network::mojom::URLResponseHeadPtr head) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TRACE_EVENT1("loading", "KeepAliveURLLoader::OnReceiveRedirect", "request_id",
request_id_);

// TODO(crbug.com/1424731): The renderer might exit before `OnReceiveRedirect`
// or `OnReceiveResponse` is called, or during their execution. In such case,
// `forwarding_client_` can't finish response handling. Figure out a way to
// negotiate shutdown timing via RenderFrameHostImpl::OnUnloadAck() and
// invalidate `forwarding_client_`.
if (forwarding_client_.is_bound() && forwarding_client_.is_connected()) {
// The renderer is alive, forwards the action.
// Redirects must be handled by the renderer so that it know what URL the
// response come from when parsing responses.
forwarding_client_->OnReceiveRedirect(redirect_info, std::move(head));
return;
}

// TODO(crbug.com/1356128): Replicates all existing behaviors from all of
// `blink::URLLoaderThrottles`.
// TODO(crbug.com/1356128): Run security checks, including CSP, mixed-content,
// and SafeBrowsing.
// TODO(crbug.com/1356128): Ask the network service to follow the redirect.
}

void KeepAliveURLLoader::OnUploadProgress(int64_t current_position,
int64_t total_size,
base::OnceCallback<void()> callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TRACE_EVENT1("loading", "KeepAliveURLLoader::OnUploadProgress", "request_id",
request_id_);

if (forwarding_client_.is_bound() && forwarding_client_.is_connected()) {
// The renderer is alive, forwards the action.
forwarding_client_->OnUploadProgress(current_position, total_size,
std::move(callback));
return;
}

// TODO(crbug.com/1356128): Handle in the browser process.
}

void KeepAliveURLLoader::OnTransferSizeUpdated(int32_t transfer_size_diff) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TRACE_EVENT1("loading", "KeepAliveURLLoader::OnTransferSizeUpdated",
"request_id", request_id_);

if (forwarding_client_.is_bound() && forwarding_client_.is_connected()) {
// The renderer is alive, forwards the action.
forwarding_client_->OnTransferSizeUpdated(transfer_size_diff);
return;
}

// TODO(crbug.com/1356128): Handle in the browser process.
}

void KeepAliveURLLoader::OnComplete(
const network::URLLoaderCompletionStatus& completion_status) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TRACE_EVENT1("loading", "KeepAliveURLLoader::OnComplete", "request_id",
request_id_);

if (forwarding_client_.is_bound() && forwarding_client_.is_connected()) {
// The renderer is alive, forwards the action.
forwarding_client_->OnComplete(completion_status);
return;
}

// TODO(crbug.com/1356128): Handle in the browser process.
}

void KeepAliveURLLoader::OnNetworkConnectionError() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TRACE_EVENT1("loading", "KeepAliveURLLoader::OnNetworkConnectionError",
"request_id", request_id_);

// The network loader has an error; we should let the client know it's
// closed by dropping this, which will in turn make this loader destroyed.
forwarding_client_.reset();
}

void KeepAliveURLLoader::OnRendererConnectionError() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TRACE_EVENT1("loading", "KeepAliveURLLoader::OnRendererConnectionError",
"request_id", request_id_);

if (has_received_response_) {
// No need to wait for `OnComplete()`.
std::move(on_delete_callback_).Run();
return;
}
// Otherwise, let this loader continue to handle responses.
forwarding_client_.reset();
// TODO(crbug.com/1424731): When we reach here while the renderer is
// processing a redirect, we should take over the redirect handling in the
// browser process. See TODOs in `OnReceiveRedirect()`.
}

} // namespace content

0 comments on commit f0e05e3

Please sign in to comment.