blob: 12b32fcd98befe37899817d8871bb53960a2fe07 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2018 The Chromium Authors
Kinuko Yasudadb67b532018-02-19 09:11:522// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/loader/prefetch_url_loader.h"
6
Kinuko Yasuda8edb92462018-02-21 15:46:577#include "base/feature_list.h"
Avi Drissmanadac21992023-01-11 23:46:398#include "base/functional/bind.h"
Dominic Farolino2131e1162019-08-09 01:15:389#include "base/metrics/histogram_functions.h"
Tsuyoshi Horo0e20a6e2019-06-04 09:48:3010#include "content/browser/web_package/prefetched_signed_exchange_cache.h"
11#include "content/browser/web_package/prefetched_signed_exchange_cache_adapter.h"
Kunihiko Sakamoto66b91d42018-06-15 08:29:2212#include "content/browser/web_package/signed_exchange_prefetch_handler.h"
Tsuyoshi Horo46f5fff2018-05-10 12:33:3513#include "content/browser/web_package/signed_exchange_utils.h"
Matt Falkenhagenf6971ae2020-06-25 08:17:4614#include "content/public/browser/browser_context.h"
Noam Rosenthal74f4a48a2022-12-15 17:15:3815#include "content/public/browser/frame_accept_header.h"
Kinuko Yasuda8edb92462018-02-21 15:46:5716#include "content/public/common/content_features.h"
Dominic Farolinodee126242019-09-09 16:58:3917#include "net/base/load_flags.h"
Brianna Goldsteind22b0642022-10-11 16:30:5018#include "net/base/network_anonymization_key.h"
Yutaka Hirano2aef1792020-02-17 23:38:1519#include "net/http/http_request_headers.h"
Kinuko Yasuda8edb92462018-02-21 15:46:5720#include "services/network/public/cpp/features.h"
Aman Verma5637b1cd2022-12-09 21:05:5521#include "services/network/public/cpp/record_ontransfersizeupdate_utils.h"
Chong Zhangb7c8d1ce2018-03-13 19:14:1122#include "services/network/public/cpp/shared_url_loader_factory.h"
Hans Wennborg78b52182021-06-15 13:42:1523#include "services/network/public/mojom/early_hints.mojom.h"
Noam Rosenthal74f4a48a2022-12-15 17:15:3824#include "services/network/public/mojom/fetch_api.mojom-shared.h"
Dominic Farolino2131e1162019-08-09 01:15:3825#include "third_party/blink/public/common/features.h"
Kinuko Yasudaeeae6972018-02-20 08:38:1126
Kinuko Yasudadb67b532018-02-19 09:11:5227namespace content {
28
Tsuyoshi Horo60be6b12018-09-29 04:28:1829namespace {
30
Noam Rosenthal74f4a48a2022-12-15 17:15:3831constexpr char kSignedExchangeEnabledAcceptHeaderForCrossOriginPrefetch[] =
Kunihiko Sakamoto1d84ea32021-09-01 04:15:5532 "application/signed-exchange;v=b3;q=0.7,*/*;q=0.8";
Tsuyoshi Horo60be6b12018-09-29 04:28:1833
34} // namespace
35
Kinuko Yasudadb67b532018-02-19 09:11:5236PrefetchURLLoader::PrefetchURLLoader(
Kinuko Yasudadb67b532018-02-19 09:11:5237 int32_t request_id,
38 uint32_t options,
Avi Drissman580a3da62024-09-04 16:16:5639 FrameTreeNodeId frame_tree_node_id,
Kinuko Yasudadb67b532018-02-19 09:11:5240 const network::ResourceRequest& resource_request,
Brianna Goldsteind22b0642022-10-11 16:30:5041 const net::NetworkAnonymizationKey& network_anonymization_key,
Julie Jeongeun Kim6dd4c7b2019-11-18 05:56:3042 mojo::PendingRemote<network::mojom::URLLoaderClient> client,
Kinuko Yasudadb67b532018-02-19 09:11:5243 const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
Chong Zhangb7c8d1ce2018-03-13 19:14:1144 scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory,
Kinuko Yasudaeeae6972018-02-20 08:38:1145 URLLoaderThrottlesGetter url_loader_throttles_getter,
Clark DuVallab63d142019-07-23 04:24:3646 BrowserContext* browser_context,
Tsuyoshi Horo242ee8f2019-04-26 06:37:0747 scoped_refptr<PrefetchedSignedExchangeCache>
48 prefetched_signed_exchange_cache,
Dominic Farolinodee126242019-09-09 16:58:3949 const std::string& accept_langs,
50 RecursivePrefetchTokenGenerator recursive_prefetch_token_generator)
Tsuyoshi Horof36fc4ef2019-10-16 10:02:1251 : frame_tree_node_id_(frame_tree_node_id),
Tsuyoshi Horod0a953d2019-01-23 04:38:1452 resource_request_(resource_request),
Brianna Goldsteind22b0642022-10-11 16:30:5053 network_anonymization_key_(network_anonymization_key),
Tsuyoshi Horocdbb4902018-04-12 06:09:1454 network_loader_factory_(std::move(network_loader_factory)),
Kinuko Yasudaeeae6972018-02-20 08:38:1155 forwarding_client_(std::move(client)),
56 url_loader_throttles_getter_(url_loader_throttles_getter),
Dominic Farolinodee126242019-09-09 16:58:3957 accept_langs_(accept_langs),
58 recursive_prefetch_token_generator_(
Clark DuVall3b2b88c52019-10-28 18:43:0959 std::move(recursive_prefetch_token_generator)),
60 is_signed_exchange_handling_enabled_(
61 signed_exchange_utils::IsSignedExchangeHandlingEnabled(
62 browser_context)) {
Kinuko Yasuda8edb92462018-02-21 15:46:5763 DCHECK(network_loader_factory_);
Andrew Williams66c039a52024-08-01 16:27:0464 CHECK(!resource_request.trusted_params ||
65 resource_request.trusted_params->isolation_info.request_type() ==
66 net::IsolationInfo::RequestType::kOther ||
67 resource_request.trusted_params->isolation_info.request_type() ==
68 net::IsolationInfo::RequestType::kMainFrame);
Kinuko Yasudadb67b532018-02-19 09:11:5269
Clark DuVall3b2b88c52019-10-28 18:43:0970 if (is_signed_exchange_handling_enabled_) {
Kunihiko Sakamoto50ed8305b2019-02-22 04:02:3171 // Set the SignedExchange accept header.
Tsuyoshi Horo60be6b12018-09-29 04:28:1872 // (https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#internet-media-type-applicationsigned-exchange).
Noam Rosenthal74f4a48a2022-12-15 17:15:3873
Alison Gale81f4f2c72024-04-22 19:33:3174 // TODO(crbug.com/40250488): find a solution for CORS requests,
Noam Rosenthal74f4a48a2022-12-15 17:15:3875 // perhaps exempt the Accept header from the 128-byte rule
76 // (https://fetch.spec.whatwg.org/#cors-safelisted-request-header). For now,
77 // we use the frame Accept header for prefetches only in requests with a
78 // no-cors/same-origin mode to avoid an unintended preflight.
79 std::string accept_header =
80 resource_request_.mode == network::mojom::RequestMode::kCors
81 ? kSignedExchangeEnabledAcceptHeaderForCrossOriginPrefetch
82 : FrameAcceptHeaderValue(/*allow_sxg_responses=*/true,
83 browser_context);
84 resource_request_.headers.SetHeader(net::HttpRequestHeaders::kAccept,
85 std::move(accept_header));
Kunihiko Sakamoto2bebcc12022-08-19 08:00:1886 if (prefetched_signed_exchange_cache) {
Tsuyoshi Horo242ee8f2019-04-26 06:37:0787 prefetched_signed_exchange_cache_adapter_ =
88 std::make_unique<PrefetchedSignedExchangeCacheAdapter>(
Clark DuValla1220f72019-08-02 19:00:5789 std::move(prefetched_signed_exchange_cache),
Lukasz Anforowicz7ef1cfd2021-05-04 02:18:3790 browser_context->GetBlobStorageContext(), resource_request.url,
91 this);
Tsuyoshi Horo242ee8f2019-04-26 06:37:0792 }
Tsuyoshi Horo60be6b12018-09-29 04:28:1893 }
94
Kinuko Yasuda8edb92462018-02-21 15:46:5795 network_loader_factory_->CreateLoaderAndStart(
Dave Tapuska28226d042021-03-17 14:21:2996 loader_.BindNewPipeAndPassReceiver(), request_id, options,
Julie Jeongeun Kim6dd4c7b2019-11-18 05:56:3097 resource_request_, client_receiver_.BindNewPipeAndPassRemote(),
98 traffic_annotation);
99 client_receiver_.set_disconnect_handler(base::BindOnce(
100 &PrefetchURLLoader::OnNetworkConnectionError, base::Unretained(this)));
Kinuko Yasudadb67b532018-02-19 09:11:52101}
102
103PrefetchURLLoader::~PrefetchURLLoader() = default;
104
Chong Zhangf19dde92018-05-23 04:33:59105void PrefetchURLLoader::FollowRedirect(
Arthur Sonzogni62a4f4352019-01-07 16:37:25106 const std::vector<std::string>& removed_headers,
107 const net::HttpRequestHeaders& modified_headers,
Takashi Toyoshimaeb2ba20c52020-04-27 16:34:58108 const net::HttpRequestHeaders& modified_cors_exempt_headers,
Arthur Sonzognic686e8f2024-01-11 08:36:37109 const std::optional<GURL>& new_url) {
Jun Cai23627f042019-01-17 05:09:10110 DCHECK(modified_headers.IsEmpty())
Arthur Sonzogni62a4f4352019-01-07 16:37:25111 << "Redirect with modified headers was not supported yet. "
112 "crbug.com/845683";
113 DCHECK(!new_url) << "Redirect with modified URL was not "
114 "supported yet. crbug.com/845683";
Kunihiko Sakamoto66b91d42018-06-15 08:29:22115 if (signed_exchange_prefetch_handler_) {
Julie Jeongeun Kim028b2c92019-11-08 02:51:16116 // Rebind |client_receiver_| and |loader_|.
117 client_receiver_.Bind(signed_exchange_prefetch_handler_->FollowRedirect(
Julie Jeongeun Kim917c197902019-11-26 03:36:14118 loader_.BindNewPipeAndPassReceiver()));
Kinuko Yasuda8edb92462018-02-21 15:46:57119 return;
120 }
121
Kinuko Yasuda39fcea92019-02-04 09:52:28122 DCHECK(loader_);
Takashi Toyoshimaeb2ba20c52020-04-27 16:34:58123 loader_->FollowRedirect(
124 removed_headers, net::HttpRequestHeaders() /* modified_headers */,
125 net::HttpRequestHeaders() /* modified_cors_exempt_headers */,
Arthur Sonzognic686e8f2024-01-11 08:36:37126 std::nullopt);
Kinuko Yasudadb67b532018-02-19 09:11:52127}
128
Kinuko Yasudadb67b532018-02-19 09:11:52129void PrefetchURLLoader::SetPriority(net::RequestPriority priority,
130 int intra_priority_value) {
Solomon Kinardab293bae2024-09-19 17:13:51131 if (loader_) {
Kinuko Yasuda39fcea92019-02-04 09:52:28132 loader_->SetPriority(priority, intra_priority_value);
Solomon Kinardab293bae2024-09-19 17:13:51133 }
Kinuko Yasudadb67b532018-02-19 09:11:52134}
135
Kenichi Ishibashi8a16c142021-03-13 02:55:33136void PrefetchURLLoader::OnReceiveEarlyHints(
137 network::mojom::EarlyHintsPtr early_hints) {
138 forwarding_client_->OnReceiveEarlyHints(std::move(early_hints));
139}
140
Kinuko Yasudadb67b532018-02-19 09:11:52141void PrefetchURLLoader::OnReceiveResponse(
John Abd-El-Malek2e0de5952022-01-25 00:12:35142 network::mojom::URLResponseHeadPtr response,
Leszek Swirski154cddb2022-08-29 17:57:19143 mojo::ScopedDataPipeConsumerHandle body,
Arthur Sonzognic686e8f2024-01-11 08:36:37144 std::optional<mojo_base::BigBuffer> cached_metadata) {
Clark DuVall3b2b88c52019-10-28 18:43:09145 if (is_signed_exchange_handling_enabled_ &&
Kunihiko Sakamotof586da62019-03-28 03:03:04146 signed_exchange_utils::ShouldHandleAsSignedHTTPExchange(
Lucas Furukawa Gadanid661c0d2019-12-02 19:58:16147 resource_request_.url, *response)) {
Kunihiko Sakamoto66b91d42018-06-15 08:29:22148 DCHECK(!signed_exchange_prefetch_handler_);
Tsuyoshi Horoec6325b82020-07-16 03:21:44149 const bool keep_entry_for_prefetch_cache =
150 !!prefetched_signed_exchange_cache_adapter_;
Matt Menke5aaa70b2020-10-30 14:03:03151
Kinuko Yasuda8edb92462018-02-21 15:46:57152 // Note that after this point this doesn't directly get upcalls from the
153 // network. (Until |this| calls the handler's FollowRedirect.)
Kunihiko Sakamoto66b91d42018-06-15 08:29:22154 signed_exchange_prefetch_handler_ =
155 std::make_unique<SignedExchangePrefetchHandler>(
Lucas Furukawa Gadanid661c0d2019-12-02 19:58:16156 frame_tree_node_id_, resource_request_, std::move(response),
John Abd-El-Malek2e0de5952022-01-25 00:12:35157 std::move(body), loader_.Unbind(), client_receiver_.Unbind(),
158 network_loader_factory_, url_loader_throttles_getter_, this,
Kunihiko Sakamotoc14fa812023-02-17 04:21:21159 network_anonymization_key_, accept_langs_,
Brianna Goldsteind22b0642022-10-11 16:30:50160 keep_entry_for_prefetch_cache);
Kinuko Yasuda8edb92462018-02-21 15:46:57161 return;
162 }
Dominic Farolinodee126242019-09-09 16:58:39163
164 // If the response is marked as a restricted cross-origin prefetch, we
165 // populate the response's |recursive_prefetch_token| member with a unique
166 // token. The renderer will propagate this token to recursive prefetches
167 // coming from this response, in the form of preload headers. This token is
168 // later used by the PrefetchURLLoaderService to recover the correct
Brianna Goldsteind22b0642022-10-11 16:30:50169 // NetworkAnonymizationKey to use when fetching the request. In the Signed
Dominic Farolinodee126242019-09-09 16:58:39170 // Exchange case, we do this after redirects from the outer response, because
171 // we redirect back here for the inner response.
Andrew Williams48e21192024-08-01 19:48:32172 if (resource_request_.load_flags &
173 net::LOAD_RESTRICTED_PREFETCH_FOR_MAIN_FRAME) {
Dominic Farolinodee126242019-09-09 16:58:39174 DCHECK(!recursive_prefetch_token_generator_.is_null());
175 base::UnguessableToken recursive_prefetch_token =
176 std::move(recursive_prefetch_token_generator_).Run(resource_request_);
177 response->recursive_prefetch_token = recursive_prefetch_token;
178 }
179
Leszek Swirski154cddb2022-08-29 17:57:19180 // Just drop any cached metadata; we don't need to forward it to the renderer
181 // for prefetch.
182 cached_metadata.reset();
183
John Abd-El-Malek998d8262022-05-10 06:33:39184 if (!body) {
185 forwarding_client_->OnReceiveResponse(std::move(response),
Leszek Swirski154cddb2022-08-29 17:57:19186 mojo::ScopedDataPipeConsumerHandle(),
Arthur Sonzognic686e8f2024-01-11 08:36:37187 std::nullopt);
John Abd-El-Malek998d8262022-05-10 06:33:39188 return;
189 }
190
191 response_ = std::move(response);
192 if (prefetched_signed_exchange_cache_adapter_ &&
193 signed_exchange_prefetch_handler_) {
194 prefetched_signed_exchange_cache_adapter_->OnStartLoadingResponseBody(
195 std::move(body));
196 return;
197 }
198
199 // Just drain the original response's body here.
200 DCHECK(!pipe_drainer_);
201 pipe_drainer_ =
202 std::make_unique<mojo::DataPipeDrainer>(this, std::move(body));
203
204 SendEmptyBody();
Kinuko Yasudadb67b532018-02-19 09:11:52205}
206
207void PrefetchURLLoader::OnReceiveRedirect(
208 const net::RedirectInfo& redirect_info,
Lucas Furukawa Gadani81e294b2019-08-29 16:26:32209 network::mojom::URLResponseHeadPtr head) {
Tsuyoshi Horo242ee8f2019-04-26 06:37:07210 if (prefetched_signed_exchange_cache_adapter_ &&
211 signed_exchange_prefetch_handler_) {
Tsuyoshi Horoec6325b82020-07-16 03:21:44212 prefetched_signed_exchange_cache_adapter_->OnReceiveSignedExchange(
213 signed_exchange_prefetch_handler_
214 ->TakePrefetchedSignedExchangeCacheEntry());
Tsuyoshi Horo242ee8f2019-04-26 06:37:07215 }
216
Tsuyoshi Horod0a953d2019-01-23 04:38:14217 resource_request_.url = redirect_info.new_url;
218 resource_request_.site_for_cookies = redirect_info.new_site_for_cookies;
Tsuyoshi Horod0a953d2019-01-23 04:38:14219 resource_request_.referrer = GURL(redirect_info.new_referrer);
220 resource_request_.referrer_policy = redirect_info.new_referrer_policy;
Lucas Furukawa Gadani81e294b2019-08-29 16:26:32221 forwarding_client_->OnReceiveRedirect(redirect_info, std::move(head));
Kinuko Yasudadb67b532018-02-19 09:11:52222}
223
Kinuko Yasudadb67b532018-02-19 09:11:52224void PrefetchURLLoader::OnUploadProgress(int64_t current_position,
225 int64_t total_size,
226 base::OnceCallback<void()> callback) {
227 forwarding_client_->OnUploadProgress(current_position, total_size,
228 std::move(callback));
229}
230
Kinuko Yasudadb67b532018-02-19 09:11:52231void PrefetchURLLoader::OnTransferSizeUpdated(int32_t transfer_size_diff) {
Aman Verma5637b1cd2022-12-09 21:05:55232 network::RecordOnTransferSizeUpdatedUMA(
233 network::OnTransferSizeUpdatedFrom::kPrefetchURLLoader);
Kinuko Yasudadb67b532018-02-19 09:11:52234 forwarding_client_->OnTransferSizeUpdated(transfer_size_diff);
235}
236
Kinuko Yasudadb67b532018-02-19 09:11:52237void PrefetchURLLoader::OnComplete(
238 const network::URLLoaderCompletionStatus& status) {
Tsuyoshi Horo242ee8f2019-04-26 06:37:07239 if (prefetched_signed_exchange_cache_adapter_ &&
240 signed_exchange_prefetch_handler_) {
241 prefetched_signed_exchange_cache_adapter_->OnComplete(status);
242 return;
243 }
244
245 SendOnComplete(status);
246}
247
248bool PrefetchURLLoader::SendEmptyBody() {
249 // Send an empty response's body.
250 mojo::ScopedDataPipeProducerHandle producer;
251 mojo::ScopedDataPipeConsumerHandle consumer;
Robert Sesek3bce5dd2021-02-19 19:27:58252 if (CreateDataPipe(nullptr, producer, consumer) != MOJO_RESULT_OK) {
Tsuyoshi Horo242ee8f2019-04-26 06:37:07253 // No more resources available for creating a data pipe. Close the
254 // connection, which will in turn make this loader destroyed.
255 forwarding_client_->OnComplete(
256 network::URLLoaderCompletionStatus(net::ERR_INSUFFICIENT_RESOURCES));
257 forwarding_client_.reset();
Julie Jeongeun Kim028b2c92019-11-08 02:51:16258 client_receiver_.reset();
Tsuyoshi Horo242ee8f2019-04-26 06:37:07259 return false;
260 }
John Abd-El-Malek998d8262022-05-10 06:33:39261 DCHECK(response_);
262 forwarding_client_->OnReceiveResponse(std::move(response_),
Arthur Sonzognic686e8f2024-01-11 08:36:37263 std::move(consumer), std::nullopt);
Tsuyoshi Horo242ee8f2019-04-26 06:37:07264 return true;
265}
266
267void PrefetchURLLoader::SendOnComplete(
268 const network::URLLoaderCompletionStatus& completion_status) {
269 forwarding_client_->OnComplete(completion_status);
Kinuko Yasudadb67b532018-02-19 09:11:52270}
271
272void PrefetchURLLoader::OnNetworkConnectionError() {
273 // The network loader has an error; we should let the client know it's closed
274 // by dropping this, which will in turn make this loader destroyed.
275 forwarding_client_.reset();
276}
277
Kinuko Yasudadb67b532018-02-19 09:11:52278} // namespace content