Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[keepalive-migration] Introduce
KeepAliveURLLoader
and its factory …
…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
Showing
7 changed files
with
1,256 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Oops, something went wrong.