1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // 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
7 #include "base/bind.h"
8 #include "base/feature_list.h"
9 #include "base/metrics/histogram_functions.h"
10 #include "content/browser/web_package/prefetched_signed_exchange_cache.h"
11 #include "content/browser/web_package/prefetched_signed_exchange_cache_adapter.h"
12 #include "content/browser/web_package/signed_exchange_prefetch_handler.h"
13 #include "content/browser/web_package/signed_exchange_prefetch_metric_recorder.h"
14 #include "content/browser/web_package/signed_exchange_utils.h"
15 #include "content/public/browser/browser_context.h"
16 #include "content/public/common/content_features.h"
17 #include "net/base/load_flags.h"
18 #include "net/base/network_isolation_key.h"
19 #include "net/http/http_request_headers.h"
20 #include "services/network/public/cpp/features.h"
21 #include "services/network/public/cpp/shared_url_loader_factory.h"
22 #include "third_party/blink/public/common/features.h"
23
24 namespace content {
25
26 namespace {
27
28 constexpr char kSignedExchangeEnabledAcceptHeaderForPrefetch[] =
29 "application/signed-exchange;v=b3;q=0.9,*/*;q=0.8";
30
31 } // namespace
32
PrefetchURLLoader(int32_t routing_id,int32_t request_id,uint32_t options,int frame_tree_node_id,const network::ResourceRequest & resource_request,const net::NetworkIsolationKey & network_isolation_key,mojo::PendingRemote<network::mojom::URLLoaderClient> client,const net::MutableNetworkTrafficAnnotationTag & traffic_annotation,scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory,URLLoaderThrottlesGetter url_loader_throttles_getter,BrowserContext * browser_context,scoped_refptr<SignedExchangePrefetchMetricRecorder> signed_exchange_prefetch_metric_recorder,scoped_refptr<PrefetchedSignedExchangeCache> prefetched_signed_exchange_cache,const std::string & accept_langs,RecursivePrefetchTokenGenerator recursive_prefetch_token_generator)33 PrefetchURLLoader::PrefetchURLLoader(
34 int32_t routing_id,
35 int32_t request_id,
36 uint32_t options,
37 int frame_tree_node_id,
38 const network::ResourceRequest& resource_request,
39 const net::NetworkIsolationKey& network_isolation_key,
40 mojo::PendingRemote<network::mojom::URLLoaderClient> client,
41 const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
42 scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory,
43 URLLoaderThrottlesGetter url_loader_throttles_getter,
44 BrowserContext* browser_context,
45 scoped_refptr<SignedExchangePrefetchMetricRecorder>
46 signed_exchange_prefetch_metric_recorder,
47 scoped_refptr<PrefetchedSignedExchangeCache>
48 prefetched_signed_exchange_cache,
49 const std::string& accept_langs,
50 RecursivePrefetchTokenGenerator recursive_prefetch_token_generator)
51 : frame_tree_node_id_(frame_tree_node_id),
52 resource_request_(resource_request),
53 network_isolation_key_(network_isolation_key),
54 network_loader_factory_(std::move(network_loader_factory)),
55 forwarding_client_(std::move(client)),
56 url_loader_throttles_getter_(url_loader_throttles_getter),
57 signed_exchange_prefetch_metric_recorder_(
58 std::move(signed_exchange_prefetch_metric_recorder)),
59 accept_langs_(accept_langs),
60 recursive_prefetch_token_generator_(
61 std::move(recursive_prefetch_token_generator)),
62 is_signed_exchange_handling_enabled_(
63 signed_exchange_utils::IsSignedExchangeHandlingEnabled(
64 browser_context)) {
65 DCHECK(network_loader_factory_);
66 DCHECK(!resource_request.trusted_params ||
67 resource_request.trusted_params->isolation_info.request_type() ==
68 net::IsolationInfo::RequestType::kOther);
69
70 if (is_signed_exchange_handling_enabled_) {
71 // Set the SignedExchange accept header.
72 // (https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#internet-media-type-applicationsigned-exchange).
73 resource_request_.headers.SetHeader(
74 net::HttpRequestHeaders::kAccept,
75 kSignedExchangeEnabledAcceptHeaderForPrefetch);
76 if (prefetched_signed_exchange_cache &&
77 resource_request.is_signed_exchange_prefetch_cache_enabled) {
78 prefetched_signed_exchange_cache_adapter_ =
79 std::make_unique<PrefetchedSignedExchangeCacheAdapter>(
80 std::move(prefetched_signed_exchange_cache),
81 BrowserContext::GetBlobStorageContext(browser_context),
82 resource_request.url, this);
83 }
84 }
85
86 network_loader_factory_->CreateLoaderAndStart(
87 loader_.BindNewPipeAndPassReceiver(), routing_id, request_id, options,
88 resource_request_, client_receiver_.BindNewPipeAndPassRemote(),
89 traffic_annotation);
90 client_receiver_.set_disconnect_handler(base::BindOnce(
91 &PrefetchURLLoader::OnNetworkConnectionError, base::Unretained(this)));
92 }
93
94 PrefetchURLLoader::~PrefetchURLLoader() = default;
95
FollowRedirect(const std::vector<std::string> & removed_headers,const net::HttpRequestHeaders & modified_headers,const net::HttpRequestHeaders & modified_cors_exempt_headers,const base::Optional<GURL> & new_url)96 void PrefetchURLLoader::FollowRedirect(
97 const std::vector<std::string>& removed_headers,
98 const net::HttpRequestHeaders& modified_headers,
99 const net::HttpRequestHeaders& modified_cors_exempt_headers,
100 const base::Optional<GURL>& new_url) {
101 DCHECK(modified_headers.IsEmpty())
102 << "Redirect with modified headers was not supported yet. "
103 "crbug.com/845683";
104 DCHECK(!new_url) << "Redirect with modified URL was not "
105 "supported yet. crbug.com/845683";
106 if (signed_exchange_prefetch_handler_) {
107 // Rebind |client_receiver_| and |loader_|.
108 client_receiver_.Bind(signed_exchange_prefetch_handler_->FollowRedirect(
109 loader_.BindNewPipeAndPassReceiver()));
110 return;
111 }
112
113 DCHECK(loader_);
114 loader_->FollowRedirect(
115 removed_headers, net::HttpRequestHeaders() /* modified_headers */,
116 net::HttpRequestHeaders() /* modified_cors_exempt_headers */,
117 base::nullopt);
118 }
119
SetPriority(net::RequestPriority priority,int intra_priority_value)120 void PrefetchURLLoader::SetPriority(net::RequestPriority priority,
121 int intra_priority_value) {
122 if (loader_)
123 loader_->SetPriority(priority, intra_priority_value);
124 }
125
PauseReadingBodyFromNet()126 void PrefetchURLLoader::PauseReadingBodyFromNet() {
127 // TODO(kinuko): Propagate or handle the case where |loader_| is
128 // detached (for SignedExchanges), see OnReceiveResponse.
129 if (loader_)
130 loader_->PauseReadingBodyFromNet();
131 }
132
ResumeReadingBodyFromNet()133 void PrefetchURLLoader::ResumeReadingBodyFromNet() {
134 // TODO(kinuko): Propagate or handle the case where |loader_| is
135 // detached (for SignedExchanges), see OnReceiveResponse.
136 if (loader_)
137 loader_->ResumeReadingBodyFromNet();
138 }
139
OnReceiveResponse(network::mojom::URLResponseHeadPtr response)140 void PrefetchURLLoader::OnReceiveResponse(
141 network::mojom::URLResponseHeadPtr response) {
142 if (is_signed_exchange_handling_enabled_ &&
143 signed_exchange_utils::ShouldHandleAsSignedHTTPExchange(
144 resource_request_.url, *response)) {
145 DCHECK(!signed_exchange_prefetch_handler_);
146 const bool keep_entry_for_prefetch_cache =
147 !!prefetched_signed_exchange_cache_adapter_;
148
149 // Note that after this point this doesn't directly get upcalls from the
150 // network. (Until |this| calls the handler's FollowRedirect.)
151 signed_exchange_prefetch_handler_ =
152 std::make_unique<SignedExchangePrefetchHandler>(
153 frame_tree_node_id_, resource_request_, std::move(response),
154 mojo::ScopedDataPipeConsumerHandle(), loader_.Unbind(),
155 client_receiver_.Unbind(), network_loader_factory_,
156 url_loader_throttles_getter_, this, network_isolation_key_,
157 signed_exchange_prefetch_metric_recorder_, accept_langs_,
158 keep_entry_for_prefetch_cache);
159 return;
160 }
161
162 // If the response is marked as a restricted cross-origin prefetch, we
163 // populate the response's |recursive_prefetch_token| member with a unique
164 // token. The renderer will propagate this token to recursive prefetches
165 // coming from this response, in the form of preload headers. This token is
166 // later used by the PrefetchURLLoaderService to recover the correct
167 // NetworkIsolationKey to use when fetching the request. In the Signed
168 // Exchange case, we do this after redirects from the outer response, because
169 // we redirect back here for the inner response.
170 if (resource_request_.load_flags & net::LOAD_RESTRICTED_PREFETCH) {
171 DCHECK(!recursive_prefetch_token_generator_.is_null());
172 base::UnguessableToken recursive_prefetch_token =
173 std::move(recursive_prefetch_token_generator_).Run(resource_request_);
174 response->recursive_prefetch_token = recursive_prefetch_token;
175 }
176
177 forwarding_client_->OnReceiveResponse(std::move(response));
178 }
179
OnReceiveRedirect(const net::RedirectInfo & redirect_info,network::mojom::URLResponseHeadPtr head)180 void PrefetchURLLoader::OnReceiveRedirect(
181 const net::RedirectInfo& redirect_info,
182 network::mojom::URLResponseHeadPtr head) {
183 if (prefetched_signed_exchange_cache_adapter_ &&
184 signed_exchange_prefetch_handler_) {
185 prefetched_signed_exchange_cache_adapter_->OnReceiveSignedExchange(
186 signed_exchange_prefetch_handler_
187 ->TakePrefetchedSignedExchangeCacheEntry());
188 }
189
190 resource_request_.url = redirect_info.new_url;
191 resource_request_.site_for_cookies = redirect_info.new_site_for_cookies;
192 resource_request_.referrer = GURL(redirect_info.new_referrer);
193 resource_request_.referrer_policy = redirect_info.new_referrer_policy;
194 forwarding_client_->OnReceiveRedirect(redirect_info, std::move(head));
195 }
196
OnUploadProgress(int64_t current_position,int64_t total_size,base::OnceCallback<void ()> callback)197 void PrefetchURLLoader::OnUploadProgress(int64_t current_position,
198 int64_t total_size,
199 base::OnceCallback<void()> callback) {
200 forwarding_client_->OnUploadProgress(current_position, total_size,
201 std::move(callback));
202 }
203
OnReceiveCachedMetadata(mojo_base::BigBuffer data)204 void PrefetchURLLoader::OnReceiveCachedMetadata(mojo_base::BigBuffer data) {
205 // Just drop this; we don't need to forward this to the renderer
206 // for prefetch.
207 }
208
OnTransferSizeUpdated(int32_t transfer_size_diff)209 void PrefetchURLLoader::OnTransferSizeUpdated(int32_t transfer_size_diff) {
210 forwarding_client_->OnTransferSizeUpdated(transfer_size_diff);
211 }
212
OnStartLoadingResponseBody(mojo::ScopedDataPipeConsumerHandle body)213 void PrefetchURLLoader::OnStartLoadingResponseBody(
214 mojo::ScopedDataPipeConsumerHandle body) {
215 if (prefetched_signed_exchange_cache_adapter_ &&
216 signed_exchange_prefetch_handler_) {
217 prefetched_signed_exchange_cache_adapter_->OnStartLoadingResponseBody(
218 std::move(body));
219 return;
220 }
221
222 // Just drain the original response's body here.
223 DCHECK(!pipe_drainer_);
224 pipe_drainer_ =
225 std::make_unique<mojo::DataPipeDrainer>(this, std::move(body));
226
227 SendEmptyBody();
228 }
229
OnComplete(const network::URLLoaderCompletionStatus & status)230 void PrefetchURLLoader::OnComplete(
231 const network::URLLoaderCompletionStatus& status) {
232 if (prefetched_signed_exchange_cache_adapter_ &&
233 signed_exchange_prefetch_handler_) {
234 prefetched_signed_exchange_cache_adapter_->OnComplete(status);
235 return;
236 }
237
238 SendOnComplete(status);
239 }
240
SendEmptyBody()241 bool PrefetchURLLoader::SendEmptyBody() {
242 // Send an empty response's body.
243 mojo::ScopedDataPipeProducerHandle producer;
244 mojo::ScopedDataPipeConsumerHandle consumer;
245 if (CreateDataPipe(nullptr, &producer, &consumer) != MOJO_RESULT_OK) {
246 // No more resources available for creating a data pipe. Close the
247 // connection, which will in turn make this loader destroyed.
248 forwarding_client_->OnComplete(
249 network::URLLoaderCompletionStatus(net::ERR_INSUFFICIENT_RESOURCES));
250 forwarding_client_.reset();
251 client_receiver_.reset();
252 return false;
253 }
254 forwarding_client_->OnStartLoadingResponseBody(std::move(consumer));
255 return true;
256 }
257
SendOnComplete(const network::URLLoaderCompletionStatus & completion_status)258 void PrefetchURLLoader::SendOnComplete(
259 const network::URLLoaderCompletionStatus& completion_status) {
260 forwarding_client_->OnComplete(completion_status);
261 }
262
OnNetworkConnectionError()263 void PrefetchURLLoader::OnNetworkConnectionError() {
264 // The network loader has an error; we should let the client know it's closed
265 // by dropping this, which will in turn make this loader destroyed.
266 forwarding_client_.reset();
267 }
268
269 } // namespace content
270