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