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/web_package/signed_exchange_loader.h"
6 
7 #include <memory>
8 
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/feature_list.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/strings/stringprintf.h"
14 #include "content/browser/web_package/signed_exchange_cert_fetcher_factory.h"
15 #include "content/browser/web_package/signed_exchange_devtools_proxy.h"
16 #include "content/browser/web_package/signed_exchange_handler.h"
17 #include "content/browser/web_package/signed_exchange_prefetch_metric_recorder.h"
18 #include "content/browser/web_package/signed_exchange_reporter.h"
19 #include "content/browser/web_package/signed_exchange_utils.h"
20 #include "content/browser/web_package/signed_exchange_validity_pinger.h"
21 #include "content/public/common/content_features.h"
22 #include "content/public/common/origin_util.h"
23 #include "net/base/load_flags.h"
24 #include "net/base/net_errors.h"
25 #include "net/cert/cert_status_flags.h"
26 #include "net/http/http_util.h"
27 #include "net/url_request/redirect_util.h"
28 #include "services/network/public/cpp/constants.h"
29 #include "services/network/public/cpp/data_pipe_to_source_stream.h"
30 #include "services/network/public/cpp/features.h"
31 #include "services/network/public/cpp/shared_url_loader_factory.h"
32 #include "services/network/public/cpp/source_stream_to_data_pipe.h"
33 #include "services/network/public/cpp/url_loader_completion_status.h"
34 #include "services/network/public/mojom/url_loader_factory.mojom.h"
35 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
36 #include "third_party/blink/public/common/web_package/web_package_request_matcher.h"
37 
38 namespace content {
39 
40 namespace {
41 
42 constexpr char kLoadResultHistogram[] = "SignedExchange.LoadResult2";
43 constexpr char kPrefetchLoadResultHistogram[] =
44     "SignedExchange.Prefetch.LoadResult2";
45 
46 SignedExchangeHandlerFactory* g_signed_exchange_factory_for_testing_ = nullptr;
47 
48 }  // namespace
49 
SignedExchangeLoader(const network::ResourceRequest & outer_request,network::mojom::URLResponseHeadPtr outer_response_head,mojo::ScopedDataPipeConsumerHandle outer_response_body,mojo::PendingRemote<network::mojom::URLLoaderClient> forwarding_client,network::mojom::URLLoaderClientEndpointsPtr endpoints,uint32_t url_loader_options,bool should_redirect_on_failure,std::unique_ptr<SignedExchangeDevToolsProxy> devtools_proxy,std::unique_ptr<SignedExchangeReporter> reporter,scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,URLLoaderThrottlesGetter url_loader_throttles_getter,int frame_tree_node_id,scoped_refptr<SignedExchangePrefetchMetricRecorder> metric_recorder,const std::string & accept_langs)50 SignedExchangeLoader::SignedExchangeLoader(
51     const network::ResourceRequest& outer_request,
52     network::mojom::URLResponseHeadPtr outer_response_head,
53     mojo::ScopedDataPipeConsumerHandle outer_response_body,
54     mojo::PendingRemote<network::mojom::URLLoaderClient> forwarding_client,
55     network::mojom::URLLoaderClientEndpointsPtr endpoints,
56     uint32_t url_loader_options,
57     bool should_redirect_on_failure,
58     std::unique_ptr<SignedExchangeDevToolsProxy> devtools_proxy,
59     std::unique_ptr<SignedExchangeReporter> reporter,
60     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
61     URLLoaderThrottlesGetter url_loader_throttles_getter,
62     int frame_tree_node_id,
63     scoped_refptr<SignedExchangePrefetchMetricRecorder> metric_recorder,
64     const std::string& accept_langs)
65     : outer_request_(outer_request),
66       outer_response_head_(std::move(outer_response_head)),
67       forwarding_client_(std::move(forwarding_client)),
68       url_loader_options_(url_loader_options),
69       should_redirect_on_failure_(should_redirect_on_failure),
70       devtools_proxy_(std::move(devtools_proxy)),
71       reporter_(std::move(reporter)),
72       url_loader_factory_(std::move(url_loader_factory)),
73       url_loader_throttles_getter_(std::move(url_loader_throttles_getter)),
74       frame_tree_node_id_(frame_tree_node_id),
75       metric_recorder_(std::move(metric_recorder)),
76       accept_langs_(accept_langs) {
77   DCHECK(outer_request_.url.is_valid());
78 
79   // |metric_recorder_| could be null in some tests.
80   if (!(outer_request_.load_flags & net::LOAD_PREFETCH) && metric_recorder_) {
81     metric_recorder_->OnSignedExchangeNonPrefetch(
82         outer_request_.url, outer_response_head_->response_time);
83   }
84   // Can't use HttpResponseHeaders::GetMimeType() because SignedExchangeHandler
85   // checks "v=" parameter.
86   outer_response_head_->headers->EnumerateHeader(nullptr, "content-type",
87                                                  &content_type_);
88 
89   url_loader_.Bind(std::move(endpoints->url_loader));
90 
91   // |outer_response_body| is valid, when it's a navigation request.
92   if (outer_response_body)
93     OnStartLoadingResponseBody(std::move(outer_response_body));
94 
95   // Bind the endpoint with |this| to get the body DataPipe.
96   url_loader_client_receiver_.Bind(std::move(endpoints->url_loader_client));
97 
98   // |client_| will be bound with a forwarding client by ConnectToClient().
99   pending_client_receiver_ = client_.BindNewPipeAndPassReceiver();
100 }
101 
102 SignedExchangeLoader::~SignedExchangeLoader() = default;
103 
OnReceiveResponse(network::mojom::URLResponseHeadPtr response_head)104 void SignedExchangeLoader::OnReceiveResponse(
105     network::mojom::URLResponseHeadPtr response_head) {
106   // Must not be called because this SignedExchangeLoader and the client
107   // endpoints were bound after OnReceiveResponse() is called.
108   NOTREACHED();
109 }
110 
OnReceiveRedirect(const net::RedirectInfo & redirect_info,network::mojom::URLResponseHeadPtr response_head)111 void SignedExchangeLoader::OnReceiveRedirect(
112     const net::RedirectInfo& redirect_info,
113     network::mojom::URLResponseHeadPtr response_head) {
114   // Must not be called because this SignedExchangeLoader and the client
115   // endpoints were bound after OnReceiveResponse() is called.
116   NOTREACHED();
117 }
118 
OnUploadProgress(int64_t current_position,int64_t total_size,OnUploadProgressCallback ack_callback)119 void SignedExchangeLoader::OnUploadProgress(
120     int64_t current_position,
121     int64_t total_size,
122     OnUploadProgressCallback ack_callback) {
123   // Must not be called because this SignedExchangeLoader and the client
124   // endpoints were bound after OnReceiveResponse() is called.
125   NOTREACHED();
126 }
127 
OnReceiveCachedMetadata(mojo_base::BigBuffer data)128 void SignedExchangeLoader::OnReceiveCachedMetadata(mojo_base::BigBuffer data) {
129   // CachedMetadata for Signed Exchange is not supported.
130   NOTREACHED();
131 }
132 
OnTransferSizeUpdated(int32_t transfer_size_diff)133 void SignedExchangeLoader::OnTransferSizeUpdated(int32_t transfer_size_diff) {
134   // TODO(https://crbug.com/803774): Implement this to progressively update the
135   // encoded data length in DevTools.
136 }
137 
OnStartLoadingResponseBody(mojo::ScopedDataPipeConsumerHandle response_body)138 void SignedExchangeLoader::OnStartLoadingResponseBody(
139     mojo::ScopedDataPipeConsumerHandle response_body) {
140   auto cert_fetcher_factory = SignedExchangeCertFetcherFactory::Create(
141       url_loader_factory_, url_loader_throttles_getter_,
142       outer_request_.throttling_profile_id,
143       outer_request_.trusted_params
144           ? base::make_optional(
145                 outer_request_.trusted_params->network_isolation_key)
146           : base::nullopt);
147 
148   if (g_signed_exchange_factory_for_testing_) {
149     signed_exchange_handler_ = g_signed_exchange_factory_for_testing_->Create(
150         outer_request_.url,
151         std::make_unique<network::DataPipeToSourceStream>(
152             std::move(response_body)),
153         base::BindOnce(&SignedExchangeLoader::OnHTTPExchangeFound,
154                        weak_factory_.GetWeakPtr()),
155         std::move(cert_fetcher_factory));
156     return;
157   }
158 
159   signed_exchange_handler_ = std::make_unique<SignedExchangeHandler>(
160       IsOriginSecure(outer_request_.url),
161       signed_exchange_utils::HasNoSniffHeader(*outer_response_head_),
162       content_type_,
163       std::make_unique<network::DataPipeToSourceStream>(
164           std::move(response_body)),
165       base::BindOnce(&SignedExchangeLoader::OnHTTPExchangeFound,
166                      weak_factory_.GetWeakPtr()),
167       std::move(cert_fetcher_factory), outer_request_.load_flags,
168       std::make_unique<blink::WebPackageRequestMatcher>(outer_request_.headers,
169                                                         accept_langs_),
170       std::move(devtools_proxy_), reporter_.get(), frame_tree_node_id_);
171 }
172 
OnComplete(const network::URLLoaderCompletionStatus & status)173 void SignedExchangeLoader::OnComplete(
174     const network::URLLoaderCompletionStatus& status) {
175   DCHECK(!outer_response_length_info_);
176   outer_response_length_info_ = OuterResponseLengthInfo();
177   outer_response_length_info_->encoded_data_length = status.encoded_data_length;
178   outer_response_length_info_->decoded_body_length = status.decoded_body_length;
179   NotifyClientOnCompleteIfReady();
180 }
181 
FollowRedirect(const std::vector<std::string> & removed_headers,const net::HttpRequestHeaders & modified_headers,const base::Optional<GURL> & new_url)182 void SignedExchangeLoader::FollowRedirect(
183     const std::vector<std::string>& removed_headers,
184     const net::HttpRequestHeaders& modified_headers,
185     const base::Optional<GURL>& new_url) {
186   NOTREACHED();
187 }
188 
SetPriority(net::RequestPriority priority,int intra_priority_value)189 void SignedExchangeLoader::SetPriority(net::RequestPriority priority,
190                                        int intra_priority_value) {
191   url_loader_->SetPriority(priority, intra_priority_value);
192 }
193 
PauseReadingBodyFromNet()194 void SignedExchangeLoader::PauseReadingBodyFromNet() {
195   url_loader_->PauseReadingBodyFromNet();
196 }
197 
ResumeReadingBodyFromNet()198 void SignedExchangeLoader::ResumeReadingBodyFromNet() {
199   url_loader_->ResumeReadingBodyFromNet();
200 }
201 
ConnectToClient(mojo::PendingRemote<network::mojom::URLLoaderClient> client)202 void SignedExchangeLoader::ConnectToClient(
203     mojo::PendingRemote<network::mojom::URLLoaderClient> client) {
204   DCHECK(pending_client_receiver_.is_valid());
205   mojo::FusePipes(std::move(pending_client_receiver_), std::move(client));
206 }
207 
208 base::Optional<net::SHA256HashValue>
ComputeHeaderIntegrity() const209 SignedExchangeLoader::ComputeHeaderIntegrity() const {
210   if (!signed_exchange_handler_)
211     return base::nullopt;
212   return signed_exchange_handler_->ComputeHeaderIntegrity();
213 }
214 
GetSignatureExpireTime() const215 base::Time SignedExchangeLoader::GetSignatureExpireTime() const {
216   if (!signed_exchange_handler_)
217     return base::Time();
218   return signed_exchange_handler_->GetSignatureExpireTime();
219 }
220 
OnHTTPExchangeFound(SignedExchangeLoadResult result,net::Error error,const GURL & request_url,network::mojom::URLResponseHeadPtr resource_response,std::unique_ptr<net::SourceStream> payload_stream)221 void SignedExchangeLoader::OnHTTPExchangeFound(
222     SignedExchangeLoadResult result,
223     net::Error error,
224     const GURL& request_url,
225     network::mojom::URLResponseHeadPtr resource_response,
226     std::unique_ptr<net::SourceStream> payload_stream) {
227   if (error) {
228     DCHECK_NE(result, SignedExchangeLoadResult::kSuccess);
229     ReportLoadResult(result);
230 
231     if (error != net::ERR_INVALID_SIGNED_EXCHANGE ||
232         !should_redirect_on_failure_ || !request_url.is_valid()) {
233       // Let the request fail.
234       // This will eventually delete |this|.
235       forwarding_client_->OnComplete(network::URLLoaderCompletionStatus(error));
236       return;
237     }
238 
239     // Make a fallback redirect to |request_url|.
240     DCHECK(!fallback_url_);
241     fallback_url_ = request_url;
242     forwarding_client_->OnReceiveRedirect(
243         signed_exchange_utils::CreateRedirectInfo(
244             request_url, outer_request_, *outer_response_head_,
245             true /* is_fallback_redirect */),
246         signed_exchange_utils::CreateRedirectResponseHead(
247             *outer_response_head_, true /* is_fallback_redirect */));
248     forwarding_client_.reset();
249     return;
250   }
251   DCHECK_EQ(result, SignedExchangeLoadResult::kSuccess);
252   inner_request_url_ = request_url;
253 
254   forwarding_client_->OnReceiveRedirect(
255       signed_exchange_utils::CreateRedirectInfo(
256           request_url, outer_request_, *outer_response_head_,
257           false /* is_fallback_redirect */),
258       signed_exchange_utils::CreateRedirectResponseHead(
259           *outer_response_head_, false /* is_fallback_redirect */));
260   forwarding_client_.reset();
261 
262   const base::Optional<net::SSLInfo>& ssl_info = resource_response->ssl_info;
263   if (ssl_info.has_value() &&
264       (url_loader_options_ &
265        network::mojom::kURLLoadOptionSendSSLInfoForCertificateError) &&
266       net::IsCertStatusError(ssl_info->cert_status)) {
267     ssl_info_ = ssl_info;
268   }
269 
270   network::mojom::URLResponseHeadPtr inner_response_head_shown_to_client =
271       std::move(resource_response);
272   if (ssl_info.has_value() &&
273       !(url_loader_options_ &
274         network::mojom::kURLLoadOptionSendSSLInfoWithResponse)) {
275     inner_response_head_shown_to_client->ssl_info = base::nullopt;
276   }
277   inner_response_head_shown_to_client->was_fetched_via_cache =
278       outer_response_head_->was_fetched_via_cache;
279   client_->OnReceiveResponse(std::move(inner_response_head_shown_to_client));
280 
281   // Currently we always assume that we have body.
282   // TODO(https://crbug.com/80374): Add error handling and bail out
283   // earlier if there's an error.
284   mojo::ScopedDataPipeProducerHandle producer_handle;
285   mojo::ScopedDataPipeConsumerHandle consumer_handle;
286   MojoCreateDataPipeOptions options;
287   options.struct_size = sizeof(MojoCreateDataPipeOptions);
288   options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE;
289   options.element_num_bytes = 1;
290   options.capacity_num_bytes = network::kDataPipeDefaultAllocationSize;
291   if (mojo::CreateDataPipe(&options, &producer_handle, &consumer_handle) !=
292       MOJO_RESULT_OK) {
293     forwarding_client_->OnComplete(
294         network::URLLoaderCompletionStatus(net::ERR_INSUFFICIENT_RESOURCES));
295     return;
296   }
297   pending_body_consumer_ = std::move(consumer_handle);
298   body_data_pipe_adapter_ = std::make_unique<network::SourceStreamToDataPipe>(
299       std::move(payload_stream), std::move(producer_handle));
300 
301   StartReadingBody();
302 }
303 
StartReadingBody()304 void SignedExchangeLoader::StartReadingBody() {
305   DCHECK(body_data_pipe_adapter_);
306   DCHECK(pending_body_consumer_.is_valid());
307 
308   // If it's not for prefetch, kSignedHTTPExchangePingValidity is enabled
309   // and validity_pinger_ is not initialized yet, create a validity pinger
310   // and start it to ping the validity URL before start reading the inner
311   // response body.
312   if (!(outer_request_.load_flags & net::LOAD_PREFETCH) &&
313       base::FeatureList::IsEnabled(features::kSignedHTTPExchangePingValidity) &&
314       !validity_pinger_) {
315     DCHECK(url_loader_factory_);
316     DCHECK(url_loader_throttles_getter_);
317     DCHECK(inner_request_url_);
318     // For now we just use the fallback (request) URL to ping.
319     // TODO(kinuko): Use the validity URL extracted from the exchange.
320     validity_pinger_ = SignedExchangeValidityPinger::CreateAndStart(
321         *inner_request_url_, url_loader_factory_,
322         url_loader_throttles_getter_.Run(),
323         outer_request_.throttling_profile_id,
324         base::BindOnce(&SignedExchangeLoader::StartReadingBody,
325                        weak_factory_.GetWeakPtr()));
326     DCHECK(validity_pinger_);
327     return;
328   }
329 
330   validity_pinger_.reset();
331 
332   // Start reading.
333   client_->OnStartLoadingResponseBody(std::move(pending_body_consumer_));
334   body_data_pipe_adapter_->Start(base::BindOnce(
335       &SignedExchangeLoader::FinishReadingBody, base::Unretained(this)));
336 }
337 
FinishReadingBody(int result)338 void SignedExchangeLoader::FinishReadingBody(int result) {
339   DCHECK(!decoded_body_read_result_);
340   decoded_body_read_result_ = result;
341   NotifyClientOnCompleteIfReady();
342 }
343 
NotifyClientOnCompleteIfReady()344 void SignedExchangeLoader::NotifyClientOnCompleteIfReady() {
345   // If |outer_response_length_info_| or |decoded_body_read_result_| is
346   // unavailable, do nothing and rely on the subsequent call to notify client.
347   if (!outer_response_length_info_ || !decoded_body_read_result_)
348     return;
349 
350   ReportLoadResult(*decoded_body_read_result_ == net::OK
351                        ? SignedExchangeLoadResult::kSuccess
352                        : SignedExchangeLoadResult::kMerkleIntegrityError);
353 
354   network::URLLoaderCompletionStatus status;
355   status.error_code = *decoded_body_read_result_;
356   status.completion_time = base::TimeTicks::Now();
357   status.encoded_data_length = outer_response_length_info_->encoded_data_length;
358   status.encoded_body_length =
359       outer_response_length_info_->decoded_body_length -
360       signed_exchange_handler_->GetExchangeHeaderLength();
361   status.decoded_body_length = body_data_pipe_adapter_->TransferredBytes();
362 
363   if (ssl_info_) {
364     DCHECK((url_loader_options_ &
365             network::mojom::kURLLoadOptionSendSSLInfoForCertificateError) &&
366            net::IsCertStatusError(ssl_info_->cert_status));
367     status.ssl_info = *ssl_info_;
368   }
369 
370   // This will eventually delete |this|.
371   client_->OnComplete(status);
372 }
373 
ReportLoadResult(SignedExchangeLoadResult result)374 void SignedExchangeLoader::ReportLoadResult(SignedExchangeLoadResult result) {
375   UMA_HISTOGRAM_ENUMERATION(kLoadResultHistogram, result);
376   // |metric_recorder_| could be null in some tests.
377   if ((outer_request_.load_flags & net::LOAD_PREFETCH) && metric_recorder_) {
378     UMA_HISTOGRAM_ENUMERATION(kPrefetchLoadResultHistogram, result);
379     metric_recorder_->OnSignedExchangePrefetchFinished(
380         outer_request_.url, outer_response_head_->response_time);
381   }
382 
383   if (reporter_)
384     reporter_->ReportResultAndFinish(result);
385 }
386 
SetSignedExchangeHandlerFactoryForTest(SignedExchangeHandlerFactory * factory)387 void SignedExchangeLoader::SetSignedExchangeHandlerFactoryForTest(
388     SignedExchangeHandlerFactory* factory) {
389   g_signed_exchange_factory_for_testing_ = factory;
390 }
391 
392 }  // namespace content
393