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