Commit 70014ad5 authored by Tsuyoshi Horo's avatar Tsuyoshi Horo Committed by Commit Bot

Fetch the certificate in SignedExchangeHandler

Bug: 803774
Change-Id: I271421221cad33a61ec1b4e329fb7e5cc53289cd
Reviewed-on: https://chromium-review.googlesource.com/911268
Commit-Queue: Tsuyoshi Horo <horo@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Cr-Commit-Position: refs/heads/master@{#536695}
parent b9a05f85
......@@ -1035,6 +1035,8 @@ jumbo_source_set("browser") {
"loader/signed_exchange_parser.h",
"loader/signed_exchange_signature_verifier.cc",
"loader/signed_exchange_signature_verifier.h",
"loader/signed_exchange_url_loader_factory_for_non_network_service.cc",
"loader/signed_exchange_url_loader_factory_for_non_network_service.h",
"loader/source_stream_to_data_pipe.cc",
"loader/source_stream_to_data_pipe.h",
"loader/stream_resource_handler.cc",
......
......@@ -22,6 +22,7 @@
#include "content/browser/loader/navigation_url_loader_delegate.h"
#include "content/browser/loader/resource_dispatcher_host_impl.h"
#include "content/browser/loader/resource_request_info_impl.h"
#include "content/browser/loader/signed_exchange_url_loader_factory_for_non_network_service.h"
#include "content/browser/loader/url_loader_request_handler.h"
#include "content/browser/loader/web_package_request_handler.h"
#include "content/browser/resource_context_impl.h"
......@@ -56,6 +57,7 @@
#include "net/url_request/redirect_util.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "services/network/loader_util.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/mojom/request_context_frame_type.mojom.h"
......@@ -308,7 +310,19 @@ class NavigationURLLoaderNetworkService::URLLoaderRequestController
default_loader_used_ = true;
if (base::FeatureList::IsEnabled(features::kSignedHTTPExchange)) {
handlers_.push_back(std::make_unique<WebPackageRequestHandler>());
DCHECK(!default_url_loader_factory_getter_);
// It is safa to pass the callback of CreateURLLoaderThrottles with the
// unretained |this|, because the passed callback will be used by a
// SignedExchangeHandler which is indirectly owned by |this| until its
// header is verified and parsed, that's where the getter is used.
handlers_.push_back(std::make_unique<WebPackageRequestHandler>(
url::Origin::Create(request_info->common_params.url),
base::MakeRefCounted<
SignedExchangeURLLoaderFactoryForNonNetworkService>(
resource_context_, url_request_context_getter),
base::BindRepeating(
&URLLoaderRequestController::CreateURLLoaderThrottles,
base::Unretained(this))));
}
// The ResourceDispatcherHostImpl can be null in unit tests.
......@@ -372,6 +386,7 @@ class NavigationURLLoaderNetworkService::URLLoaderRequestController
}
void Start(
net::URLRequestContextGetter* url_request_context_getter,
ServiceWorkerNavigationHandleCore* service_worker_navigation_handle_core,
AppCacheNavigationHandleCore* appcache_handle_core,
std::unique_ptr<NavigationRequestInfo> request_info,
......@@ -403,10 +418,7 @@ class NavigationURLLoaderNetworkService::URLLoaderRequestController
url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart(
base::MakeRefCounted<WrapperSharedURLLoaderFactory>(
std::move(factory_for_webui)),
GetContentClient()->browser()->CreateURLLoaderThrottles(
*resource_request_, resource_context_, web_contents_getter_,
navigation_ui_data_.get(), frame_tree_node_id_),
0 /* routing_id */, 0 /* request_id? */,
CreateURLLoaderThrottles(), 0 /* routing_id */, 0 /* request_id? */,
network::mojom::kURLLoadOptionNone, resource_request_.get(), this,
kNavigationUrlLoaderTrafficAnnotation,
base::ThreadTaskRunnerHandle::Get());
......@@ -443,7 +455,17 @@ class NavigationURLLoaderNetworkService::URLLoaderRequestController
}
if (base::FeatureList::IsEnabled(features::kSignedHTTPExchange)) {
handlers_.push_back(std::make_unique<WebPackageRequestHandler>());
// It is safa to pass the callback of CreateURLLoaderThrottles with the
// unretained |this|, because the passed callback will be used by a
// SignedExchangeHandler which is indirectly owned by |this| until its
// header is verified and parsed, that's where the getter is used.
handlers_.push_back(std::make_unique<WebPackageRequestHandler>(
url::Origin::Create(request_info->common_params.url),
base::MakeRefCounted<WeakWrapperSharedURLLoaderFactory>(
default_url_loader_factory_getter_->GetNetworkFactory()),
base::BindRepeating(
&URLLoaderRequestController::CreateURLLoaderThrottles,
base::Unretained(this))));
}
Restart();
......@@ -479,10 +501,7 @@ class NavigationURLLoaderNetworkService::URLLoaderRequestController
url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart(
base::MakeRefCounted<SingleRequestURLLoaderFactory>(
std::move(single_request_handler)),
GetContentClient()->browser()->CreateURLLoaderThrottles(
*resource_request_, resource_context_, web_contents_getter_,
navigation_ui_data_.get(), frame_tree_node_id_),
frame_tree_node_id_, 0 /* request_id? */,
CreateURLLoaderThrottles(), frame_tree_node_id_, 0 /* request_id? */,
network::mojom::kURLLoadOptionNone, resource_request_.get(), this,
kNavigationUrlLoaderTrafficAnnotation,
base::ThreadTaskRunnerHandle::Get());
......@@ -556,11 +575,9 @@ class NavigationURLLoaderNetworkService::URLLoaderRequestController
// getters.
url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart(
base::MakeRefCounted<WeakWrapperSharedURLLoaderFactory>(factory),
GetContentClient()->browser()->CreateURLLoaderThrottles(
*resource_request_, resource_context_, web_contents_getter_,
navigation_ui_data_.get(), frame_tree_node_id_),
frame_tree_node_id_, 0 /* request_id? */, options,
resource_request_.get(), this, kNavigationUrlLoaderTrafficAnnotation,
CreateURLLoaderThrottles(), frame_tree_node_id_, 0 /* request_id? */,
options, resource_request_.get(), this,
kNavigationUrlLoaderTrafficAnnotation,
base::ThreadTaskRunnerHandle::Get());
}
......@@ -786,6 +803,13 @@ class NavigationURLLoaderNetworkService::URLLoaderRequestController
return false;
}
std::vector<std::unique_ptr<content::URLLoaderThrottle>>
CreateURLLoaderThrottles() {
return GetContentClient()->browser()->CreateURLLoaderThrottles(
*resource_request_, resource_context_, web_contents_getter_,
navigation_ui_data_.get(), frame_tree_node_id_);
}
std::vector<std::unique_ptr<URLLoaderRequestHandler>> handlers_;
size_t handler_index_ = 0;
......@@ -905,7 +929,7 @@ NavigationURLLoaderNetworkService::NavigationURLLoaderNetworkService(
base::BindOnce(
&URLLoaderRequestController::StartWithoutNetworkService,
base::Unretained(request_controller_.get()),
base::Unretained(storage_partition->GetURLRequestContext()),
base::RetainedRef(storage_partition->GetURLRequestContext()),
base::Unretained(storage_partition->GetFileSystemContext()),
base::Unretained(service_worker_navigation_handle_core),
base::Unretained(appcache_handle_core),
......@@ -939,6 +963,7 @@ NavigationURLLoaderNetworkService::NavigationURLLoaderNetworkService(
base::BindOnce(
&URLLoaderRequestController::Start,
base::Unretained(request_controller_.get()),
base::RetainedRef(storage_partition->GetURLRequestContext()),
service_worker_navigation_handle_core, appcache_handle_core,
std::move(request_info), std::move(navigation_ui_data),
std::move(factory_for_webui), frame_tree_node_id,
......
......@@ -746,7 +746,9 @@ void ResourceDispatcherHostImpl::OnRequestResourceInternal(
network::mojom::URLLoaderRequest mojo_request,
network::mojom::URLLoaderClientPtr url_loader_client,
const net::NetworkTrafficAnnotationTag& traffic_annotation) {
DCHECK(requester_info->IsRenderer() || requester_info->IsNavigationPreload());
DCHECK(requester_info->IsRenderer() ||
requester_info->IsNavigationPreload() ||
requester_info->IsCertificateFetcherForSignedExchange());
BeginRequest(requester_info, request_id, request_data, is_sync_load,
routing_id, std::move(mojo_request),
std::move(url_loader_client), traffic_annotation);
......@@ -901,7 +903,9 @@ void ResourceDispatcherHostImpl::BeginRequest(
network::mojom::URLLoaderRequest mojo_request,
network::mojom::URLLoaderClientPtr url_loader_client,
const net::NetworkTrafficAnnotationTag& traffic_annotation) {
DCHECK(requester_info->IsRenderer() || requester_info->IsNavigationPreload());
DCHECK(requester_info->IsRenderer() ||
requester_info->IsNavigationPreload() ||
requester_info->IsCertificateFetcherForSignedExchange());
int child_id = requester_info->child_id();
// Reject request id that's currently in use.
......@@ -1041,7 +1045,9 @@ void ResourceDispatcherHostImpl::ContinuePendingBeginRequest(
BlobHandles blob_handles,
const net::NetworkTrafficAnnotationTag& traffic_annotation,
HeaderInterceptorResult interceptor_result) {
DCHECK(requester_info->IsRenderer() || requester_info->IsNavigationPreload());
DCHECK(requester_info->IsRenderer() ||
requester_info->IsNavigationPreload() ||
requester_info->IsCertificateFetcherForSignedExchange());
if (interceptor_result != HeaderInterceptorResult::CONTINUE) {
if (requester_info->IsRenderer() &&
interceptor_result == HeaderInterceptorResult::KILL) {
......@@ -1297,7 +1303,9 @@ ResourceDispatcherHostImpl::CreateResourceHandler(
ResourceContext* resource_context,
network::mojom::URLLoaderRequest mojo_request,
network::mojom::URLLoaderClientPtr url_loader_client) {
DCHECK(requester_info->IsRenderer() || requester_info->IsNavigationPreload());
DCHECK(requester_info->IsRenderer() ||
requester_info->IsNavigationPreload() ||
requester_info->IsCertificateFetcherForSignedExchange());
// Construct the IPC resource handler.
std::unique_ptr<ResourceHandler> handler;
handler = CreateBaseResourceHandler(
......
......@@ -4,6 +4,7 @@
#include "content/browser/loader/resource_requester_info.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "content/browser/appcache/chrome_appcache_service.h"
......@@ -11,6 +12,8 @@
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/public/browser/resource_context.h"
#include "content/public/common/browser_side_navigation_policy.h"
#include "content/public/common/content_features.h"
#include "services/network/public/cpp/features.h"
#include "storage/browser/fileapi/file_system_context.h"
namespace content {
......@@ -125,4 +128,16 @@ ResourceRequesterInfo::CreateForNavigationPreload(
original_request_info->service_worker_context(), get_contexts_callback));
}
scoped_refptr<ResourceRequesterInfo>
ResourceRequesterInfo::CreateForCertificateFetcherForSignedExchange(
const GetContextsCallback& get_contexts_callback) {
DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService));
DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange));
return scoped_refptr<ResourceRequesterInfo>(new ResourceRequesterInfo(
RequesterType::CERTIFICATE_FETCHER_FOR_SIGNED_EXCHANGE, -1,
nullptr /* appcache_service */, nullptr /* blob_storage_context */,
nullptr /* file_system_context */, nullptr /* service_worker_context */,
get_contexts_callback));
}
} // namespace content
......@@ -74,6 +74,13 @@ class CONTENT_EXPORT ResourceRequesterInfo
static scoped_refptr<ResourceRequesterInfo> CreateForNavigationPreload(
ResourceRequesterInfo* original_request_info);
// Creates a ResourceRequesterInfo for a requester that requests certificates
// for signed exchange.
// https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html
static scoped_refptr<ResourceRequesterInfo>
CreateForCertificateFetcherForSignedExchange(
const GetContextsCallback& get_contexts_callback);
bool IsRenderer() const { return type_ == RequesterType::RENDERER; }
bool IsBrowserSideNavigation() const {
return type_ == RequesterType::BROWSER_SIDE_NAVIGATION;
......@@ -81,6 +88,9 @@ class CONTENT_EXPORT ResourceRequesterInfo
bool IsNavigationPreload() const {
return type_ == RequesterType::NAVIGATION_PRELOAD;
}
bool IsCertificateFetcherForSignedExchange() const {
return type_ == RequesterType::CERTIFICATE_FETCHER_FOR_SIGNED_EXCHANGE;
}
// Returns the renderer process ID associated with the requester. Returns -1
// for browser side navigation requester. Even if the ResourceMessageFilter
......@@ -134,7 +144,8 @@ class CONTENT_EXPORT ResourceRequesterInfo
RENDERER,
BROWSER_SIDE_NAVIGATION,
DOWNLOAD_OR_PAGE_SAVE,
NAVIGATION_PRELOAD
NAVIGATION_PRELOAD,
CERTIFICATE_FETCHER_FOR_SIGNED_EXCHANGE
};
ResourceRequesterInfo(RequesterType type,
......
......@@ -90,12 +90,13 @@ SignedExchangeCertFetcher::CreateAndStart(
scoped_refptr<SharedURLLoaderFactory> shared_url_loader_factory,
std::vector<std::unique_ptr<URLLoaderThrottle>> throttles,
const GURL& cert_url,
url::Origin request_initiator,
bool force_fetch,
CertificateCallback callback) {
std::unique_ptr<SignedExchangeCertFetcher> cert_fetcher(
new SignedExchangeCertFetcher(std::move(shared_url_loader_factory),
std::move(throttles), cert_url, force_fetch,
std::move(callback)));
new SignedExchangeCertFetcher(
std::move(shared_url_loader_factory), std::move(throttles), cert_url,
std::move(request_initiator), force_fetch, std::move(callback)));
cert_fetcher->Start();
return cert_fetcher;
}
......@@ -159,6 +160,7 @@ SignedExchangeCertFetcher::SignedExchangeCertFetcher(
scoped_refptr<SharedURLLoaderFactory> shared_url_loader_factory,
std::vector<std::unique_ptr<URLLoaderThrottle>> throttles,
const GURL& cert_url,
url::Origin request_initiator,
bool force_fetch,
CertificateCallback callback)
: shared_url_loader_factory_(std::move(shared_url_loader_factory)),
......@@ -167,6 +169,7 @@ SignedExchangeCertFetcher::SignedExchangeCertFetcher(
callback_(std::move(callback)) {
// TODO(https://crbug.com/803774): Revisit more ResourceRequest flags.
resource_request_->url = cert_url;
resource_request_->request_initiator = std::move(request_initiator);
resource_request_->resource_type = RESOURCE_TYPE_SUB_RESOURCE;
// Cert requests should not send credential informartion, because the default
// credentials mode of Fetch is "omit".
......
......@@ -15,6 +15,7 @@
#include "base/strings/string_piece_forward.h"
#include "content/common/content_export.h"
#include "services/network/public/mojom/url_loader.mojom.h"
#include "url/origin.h"
namespace net {
class X509Certificate;
......@@ -45,6 +46,7 @@ class CONTENT_EXPORT SignedExchangeCertFetcher
scoped_refptr<SharedURLLoaderFactory> shared_url_loader_factory,
std::vector<std::unique_ptr<URLLoaderThrottle>> throttles,
const GURL& cert_url,
url::Origin request_initiator,
bool force_fetch,
CertificateCallback callback);
......@@ -69,6 +71,7 @@ class CONTENT_EXPORT SignedExchangeCertFetcher
scoped_refptr<SharedURLLoaderFactory> shared_url_loader_factory,
std::vector<std::unique_ptr<URLLoaderThrottle>> throttles,
const GURL& cert_url,
url::Origin request_initiator,
bool force_fetch,
CertificateCallback callback);
void Start();
......
......@@ -143,7 +143,9 @@ void ForwardCertificateCallback(bool* called,
class SignedExchangeCertFetcherTest : public testing::Test {
public:
SignedExchangeCertFetcherTest()
: url_(GURL("https://www.example.com/cert")) {}
: url_(GURL("https://www.example.com/cert")),
request_initiator_(
url::Origin::Create(GURL("https://htxg.example.com/test.htxg"))) {}
~SignedExchangeCertFetcherTest() override {}
protected:
......@@ -209,7 +211,8 @@ class SignedExchangeCertFetcherTest : public testing::Test {
return SignedExchangeCertFetcher::CreateAndStart(
base::MakeRefCounted<WeakWrapperSharedURLLoaderFactory>(
&mock_loader_factory_),
std::move(throttles_), url_, force_fetch, std::move(callback));
std::move(throttles_), url_, request_initiator_, force_fetch,
std::move(callback));
}
void CallOnReceiveResponse() {
......@@ -233,6 +236,7 @@ class SignedExchangeCertFetcherTest : public testing::Test {
void CloseClientPipe() { mock_loader_factory_.CloseClientPipe(); }
const GURL url_;
const url::Origin request_initiator_;
bool callback_called_ = false;
scoped_refptr<net::X509Certificate> cert_result_;
URLLoaderFactoryForMockLoader mock_loader_factory_;
......@@ -500,6 +504,8 @@ TEST_F(SignedExchangeCertFetcherTest, Simple) {
EXPECT_EQ(net::LOAD_DO_NOT_SEND_AUTH_DATA | net::LOAD_DO_NOT_SAVE_COOKIES |
net::LOAD_DO_NOT_SEND_COOKIES,
mock_loader_factory_.url_request()->load_flags);
EXPECT_EQ(request_initiator_,
mock_loader_factory_.url_request()->request_initiator);
CallOnReceiveResponse();
mock_loader_factory_.client_ptr()->OnStartLoadingResponseBody(
......
......@@ -7,13 +7,19 @@
#include "base/feature_list.h"
#include "components/cbor/cbor_reader.h"
#include "content/browser/loader/merkle_integrity_source_stream.h"
#include "content/browser/loader/signed_exchange_cert_fetcher.h"
#include "content/browser/loader/signed_exchange_consts.h"
#include "content/browser/loader/signed_exchange_header_parser.h"
#include "content/public/common/content_features.h"
#include "content/public/common/shared_url_loader_factory.h"
#include "content/public/common/url_loader_throttle.h"
#include "mojo/public/cpp/system/string_data_pipe_producer.h"
#include "net/base/io_buffer.h"
#include "net/cert/x509_certificate.h"
#include "net/filter/source_stream.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_util.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/resource_response.h"
#include "services/network/public/cpp/url_loader_completion_status.h"
......@@ -61,9 +67,15 @@ class BufferSourceStream : public net::SourceStream {
SignedExchangeHandler::SignedExchangeHandler(
std::unique_ptr<net::SourceStream> body,
ExchangeHeadersCallback headers_callback)
ExchangeHeadersCallback headers_callback,
url::Origin request_initiator,
scoped_refptr<SharedURLLoaderFactory> url_loader_factory,
URLLoaderThrottlesGetter url_loader_throttles_getter)
: headers_callback_(std::move(headers_callback)),
source_(std::move(body)),
request_initiator_(std::move(request_initiator)),
url_loader_factory_(std::move(url_loader_factory)),
url_loader_throttles_getter_(std::move(url_loader_throttles_getter)),
weak_factory_(this) {
DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange));
......@@ -196,6 +208,8 @@ bool SignedExchangeHandler::RunHeadersCallback() {
base::StringPiece status_code_str =
status_iter->second.GetBytestringAsString();
base::Optional<std::vector<SignedExchangeHeaderParser::Signature>> signatures;
std::string fake_header_str("HTTP/1.1 ");
status_code_str.AppendToString(&fake_header_str);
fake_header_str.append(" OK\r\n");
......@@ -213,6 +227,9 @@ bool SignedExchangeHandler::RunHeadersCallback() {
fake_header_str.append(": ");
value.AppendToString(&fake_header_str);
fake_header_str.append("\r\n");
if (std::string(name) == "signature") {
signatures = SignedExchangeHeaderParser::ParseSignature(value);
}
}
fake_header_str.append("\r\n");
response_head_.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
......@@ -231,12 +248,21 @@ bool SignedExchangeHandler::RunHeadersCallback() {
return false;
}
auto payload_stream = std::make_unique<BufferSourceStream>(payload_bytes);
auto mi_stream = std::make_unique<MerkleIntegritySourceStream>(
mi_stream_ = std::make_unique<MerkleIntegritySourceStream>(
mi_header_value, std::move(payload_stream));
std::move(headers_callback_)
.Run(net::OK, request_url_, request_method_, response_head_,
std::move(mi_stream), ssl_info_);
if (!signatures || signatures->empty())
return false;
DCHECK(url_loader_factory_);
DCHECK(url_loader_throttles_getter_);
std::vector<std::unique_ptr<URLLoaderThrottle>> throttles =
std::move(url_loader_throttles_getter_).Run();
cert_fetcher_ = SignedExchangeCertFetcher::CreateAndStart(
std::move(url_loader_factory_), std::move(throttles),
GURL((*signatures)[0].cert_url), std::move(request_initiator_), false,
base::BindOnce(&SignedExchangeHandler::OnCertReceived,
base::Unretained(this)));
return true;
}
......@@ -247,4 +273,18 @@ void SignedExchangeHandler::RunErrorCallback(net::Error error) {
nullptr, base::nullopt);
}
void SignedExchangeHandler::OnCertReceived(
scoped_refptr<net::X509Certificate> cert) {
if (!cert) {
DVLOG(1) << "Fetching certificate error";
RunErrorCallback(net::ERR_FAILED);
return;
}
// TODO(https://crbug.com/803774): Verify the certificate and generate a
// SSLInfo.
std::move(headers_callback_)
.Run(net::OK, request_url_, request_method_, response_head_,
std::move(mi_stream_), base::Optional<net::SSLInfo>());
}
} // namespace content
......@@ -9,11 +9,13 @@
#include "base/callback.h"
#include "base/optional.h"
#include "content/public/common/shared_url_loader_factory.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "net/base/completion_callback.h"
#include "net/ssl/ssl_info.h"
#include "services/network/public/cpp/resource_response.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace net {
class SourceStream;
......@@ -21,6 +23,11 @@ class SourceStream;
namespace content {
class SharedURLLoaderFactory;
class SignedExchangeCertFetcher;
class URLLoaderThrottle;
class MerkleIntegritySourceStream;
// IMPORTANT: Currenly SignedExchangeHandler doesn't implement any verifying
// logic.
// TODO(https://crbug.com/803774): Implement verifying logic.
......@@ -35,11 +42,20 @@ class SignedExchangeHandler final {
std::unique_ptr<net::SourceStream> payload_stream,
base::Optional<net::SSLInfo>)>;
using URLLoaderThrottlesGetter = base::RepeatingCallback<
std::vector<std::unique_ptr<content::URLLoaderThrottle>>()>;
// Once constructed |this| starts reading the |body| and parses the response
// as a signed HTTP exchange. The response body of the exchange can be read
// from |payload_stream| passed to |headers_callback|.
SignedExchangeHandler(std::unique_ptr<net::SourceStream> body,
ExchangeHeadersCallback headers_callback);
// from |payload_stream| passed to |headers_callback|. |url_loader_factory|
// and |url_loader_throttles_getter| are used to set up a network URLLoader to
// actually fetch the certificate.
SignedExchangeHandler(
std::unique_ptr<net::SourceStream> body,
ExchangeHeadersCallback headers_callback,
url::Origin request_initiator,
scoped_refptr<SharedURLLoaderFactory> url_loader_factory,
URLLoaderThrottlesGetter url_loader_throttles_getter);
~SignedExchangeHandler();
private:
......@@ -48,11 +64,12 @@ class SignedExchangeHandler final {
bool RunHeadersCallback();
void RunErrorCallback(net::Error);
void OnCertReceived(scoped_refptr<net::X509Certificate> cert);
// Signed exchange contents.
GURL request_url_;
std::string request_method_;
network::ResourceResponseHead response_head_;
base::Optional<net::SSLInfo> ssl_info_;
ExchangeHeadersCallback headers_callback_;
std::unique_ptr<net::SourceStream> source_;
......@@ -62,6 +79,17 @@ class SignedExchangeHandler final {
scoped_refptr<net::IOBufferWithSize> read_buf_;
std::string original_body_string_;
std::unique_ptr<MerkleIntegritySourceStream> mi_stream_;
// Used to create |cert_fetcher_|.
url::Origin request_initiator_;
scoped_refptr<SharedURLLoaderFactory> url_loader_factory_;
// This getter is guaranteed to be valid at least until the headers callback
// is run.
URLLoaderThrottlesGetter url_loader_throttles_getter_;
std::unique_ptr<SignedExchangeCertFetcher> cert_fetcher_;
base::WeakPtrFactory<SignedExchangeHandler> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(SignedExchangeHandler);
......
......@@ -199,7 +199,7 @@ SignedExchangeHeaderParser::ParseSignedHeaders(
}
base::Optional<std::vector<SignedExchangeHeaderParser::Signature>>
SignedExchangeHeaderParser::ParseSignature(const std::string& signature_str) {
SignedExchangeHeaderParser::ParseSignature(base::StringPiece signature_str) {
StructuredHeaderParser parser(signature_str);
std::vector<ParameterisedLabel> values;
parser.ParseParameterisedLabelList(&values);
......
......@@ -10,6 +10,7 @@
#include <vector>
#include "base/macros.h"
#include "base/optional.h"
#include "base/strings/string_piece.h"
#include "content/common/content_export.h"
namespace content {
......@@ -42,7 +43,7 @@ class CONTENT_EXPORT SignedExchangeHeaderParser {
// Parses a value of the Signature header.
// https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#rfc.section.3.2
static base::Optional<std::vector<Signature>> ParseSignature(
const std::string& signature_str);
base::StringPiece signature_str);
};
} // namespace content
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/loader/signed_exchange_url_loader_factory_for_non_network_service.h"
#include "base/feature_list.h"
#include "content/browser/loader/resource_requester_info.h"
#include "content/browser/loader/url_loader_factory_impl.h"
#include "content/public/common/content_features.h"
#include "net/url_request/url_request_context_getter.h"
#include "services/network/public/cpp/features.h"
namespace content {
SignedExchangeURLLoaderFactoryForNonNetworkService::
SignedExchangeURLLoaderFactoryForNonNetworkService(
ResourceContext* resource_context,
net::URLRequestContextGetter* url_request_context_getter)
: resource_context_(resource_context),
url_request_context_getter_(url_request_context_getter) {
DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService));
DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange));
}
SignedExchangeURLLoaderFactoryForNonNetworkService::
~SignedExchangeURLLoaderFactoryForNonNetworkService() = default;
void SignedExchangeURLLoaderFactoryForNonNetworkService::CreateLoaderAndStart(
network::mojom::URLLoaderRequest loader_request,
int32_t routing_id,
int32_t request_id,
uint32_t options,
const network::ResourceRequest& request,
network::mojom::URLLoaderClientPtr client,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
const Constraints& constraints) {
if (!url_request_context_getter_->GetURLRequestContext()) {
// The context has been destroyed.
return;
}
std::unique_ptr<URLLoaderFactoryImpl> url_loader_factory =
std::make_unique<URLLoaderFactoryImpl>(
ResourceRequesterInfo::CreateForCertificateFetcherForSignedExchange(
base::BindRepeating(
&SignedExchangeURLLoaderFactoryForNonNetworkService::
GetContextsCallback,
base::Unretained(this))));
url_loader_factory->CreateLoaderAndStart(
std::move(loader_request), routing_id, request_id, options, request,
std::move(client), traffic_annotation);
}
std::unique_ptr<SharedURLLoaderFactoryInfo>
SignedExchangeURLLoaderFactoryForNonNetworkService::Clone() {
NOTREACHED();
return nullptr;
}
void SignedExchangeURLLoaderFactoryForNonNetworkService::GetContextsCallback(
ResourceType resource_type,
ResourceContext** resource_context_out,
net::URLRequestContext** request_context_out) {
DCHECK(url_request_context_getter_->GetURLRequestContext());
*resource_context_out = resource_context_;
*request_context_out = url_request_context_getter_->GetURLRequestContext();
}
} // namespace content
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_LOADER_SIGNED_EXCHANGE_URL_LOADER_FACTORY_FOR_NON_NETWORK_SERVICE_H_
#define CONTENT_BROWSER_LOADER_SIGNED_EXCHANGE_URL_LOADER_FACTORY_FOR_NON_NETWORK_SERVICE_H_
#include "content/public/common/resource_type.h"
#include "content/public/common/shared_url_loader_factory.h"
namespace net {
class URLRequestContextGetter;
} // namespace net
namespace content {
class URLRequestContextGetter;
class ResourceContext;
// A URLLoaderFactory used for fetching certificate of signed HTTP exchange
// when NetworkService is not enabled.
class SignedExchangeURLLoaderFactoryForNonNetworkService
: public SharedURLLoaderFactory {
public:
SignedExchangeURLLoaderFactoryForNonNetworkService(
ResourceContext* resource_context,
net::URLRequestContextGetter* url_request_context_getter);
// SharedURLLoaderFactory:
void CreateLoaderAndStart(
network::mojom::URLLoaderRequest loader_request,
int32_t routing_id,
int32_t request_id,
uint32_t options,
const network::ResourceRequest& request,
network::mojom::URLLoaderClientPtr client,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
const Constraints& constraints) override;
std::unique_ptr<SharedURLLoaderFactoryInfo> Clone() override;
private:
~SignedExchangeURLLoaderFactoryForNonNetworkService() override;
void GetContextsCallback(ResourceType resource_type,
ResourceContext** resource_context_out,
net::URLRequestContext** request_context_out);
ResourceContext* resource_context_;
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
DISALLOW_COPY_AND_ASSIGN(SignedExchangeURLLoaderFactoryForNonNetworkService);
};
} // namespace content
#endif // CONTENT_BROWSER_LOADER_SIGNED_EXCHANGE_URL_LOADER_FACTORY_FOR_NON_NETWORK_SERVICE_H_
......@@ -20,7 +20,8 @@ URLLoaderFactoryImpl::URLLoaderFactoryImpl(
: requester_info_(std::move(requester_info)) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK((requester_info_->IsRenderer() && requester_info_->filter()) ||
requester_info_->IsNavigationPreload());
requester_info_->IsNavigationPreload() ||
requester_info_->IsCertificateFetcherForSignedExchange());
}
URLLoaderFactoryImpl::~URLLoaderFactoryImpl() {
......
......@@ -12,6 +12,7 @@
#include "content/browser/loader/signed_exchange_handler.h"
#include "content/browser/loader/source_stream_to_data_pipe.h"
#include "content/public/common/content_features.h"
#include "content/public/common/shared_url_loader_factory.h"
#include "net/http/http_util.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/url_loader_completion_status.h"
......@@ -70,11 +71,17 @@ class WebPackageLoader::ResponseTimingInfo {
WebPackageLoader::WebPackageLoader(
const network::ResourceResponseHead& original_response,
network::mojom::URLLoaderClientPtr forwarding_client,
network::mojom::URLLoaderClientEndpointsPtr endpoints)
network::mojom::URLLoaderClientEndpointsPtr endpoints,
url::Origin request_initiator,
scoped_refptr<SharedURLLoaderFactory> url_loader_factory,
URLLoaderThrottlesGetter url_loader_throttles_getter)
: original_response_timing_info_(
std::make_unique<ResponseTimingInfo>(original_response)),
forwarding_client_(std::move(forwarding_client)),
url_loader_client_binding_(this),
request_initiator_(request_initiator),
url_loader_factory_(std::move(url_loader_factory)),
url_loader_throttles_getter_(std::move(url_loader_throttles_getter)),
weak_factory_(this) {
DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange));
url_loader_.Bind(std::move(endpoints->url_loader));
......@@ -145,7 +152,9 @@ void WebPackageLoader::OnStartLoadingResponseBody(
signed_exchange_handler_ = std::make_unique<SignedExchangeHandler>(
std::make_unique<DataPipeToSourceStream>(std::move(body)),
base::BindOnce(&WebPackageLoader::OnHTTPExchangeFound,
weak_factory_.GetWeakPtr()));
weak_factory_.GetWeakPtr()),
std::move(request_initiator_), std::move(url_loader_factory_),
std::move(url_loader_throttles_getter_));
}
void WebPackageLoader::OnComplete(
......@@ -198,7 +207,7 @@ void WebPackageLoader::OnHTTPExchangeFound(
base::Optional<net::SSLInfo> ssl_info) {
if (error) {
// This will eventually delete |this|.
client_->OnComplete(network::URLLoaderCompletionStatus(error));
forwarding_client_->OnComplete(network::URLLoaderCompletionStatus(error));
return;
}
......
......@@ -12,6 +12,7 @@
#include "net/url_request/redirect_info.h"
#include "services/network/public/cpp/net_adapters.h"
#include "services/network/public/mojom/url_loader.mojom.h"
#include "url/origin.h"
namespace net {
class SourceStream;
......@@ -19,6 +20,8 @@ class SourceStream;
namespace content {
class SharedURLLoaderFactory;
class URLLoaderThrottle;
class SignedExchangeHandler;
class SourceStreamToDataPipe;
......@@ -30,9 +33,15 @@ class SourceStreamToDataPipe;
class WebPackageLoader final : public network::mojom::URLLoaderClient,
public network::mojom::URLLoader {
public:
using URLLoaderThrottlesGetter = base::RepeatingCallback<
std::vector<std::unique_ptr<content::URLLoaderThrottle>>()>;
WebPackageLoader(const network::ResourceResponseHead& original_response,
network::mojom::URLLoaderClientPtr forwarding_client,
network::mojom::URLLoaderClientEndpointsPtr endpoints);
network::mojom::URLLoaderClientEndpointsPtr endpoints,
url::Origin request_initiator,
scoped_refptr<SharedURLLoaderFactory> url_loader_factory,
URLLoaderThrottlesGetter url_loader_throttles_getter);
~WebPackageLoader() override;
// network::mojom::URLLoaderClient implementation
......@@ -103,6 +112,10 @@ class WebPackageLoader final : public network::mojom::URLLoaderClient,
// Kept around until ProceedWithResponse is called.
mojo::ScopedDataPipeConsumerHandle pending_body_consumer_;
url::Origin request_initiator_;
scoped_refptr<SharedURLLoaderFactory> url_loader_factory_;
URLLoaderThrottlesGetter url_loader_throttles_getter_;
base::WeakPtrFactory<WebPackageLoader> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(WebPackageLoader);
......
......@@ -11,6 +11,7 @@
#include "content/browser/loader/web_package_loader.h"
#include "content/common/throttling_url_loader.h"
#include "content/public/common/content_features.h"
#include "content/public/common/shared_url_loader_factory.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "net/http/http_response_headers.h"
#include "services/network/public/cpp/resource_response.h"
......@@ -25,7 +26,14 @@ bool WebPackageRequestHandler::IsSupportedMimeType(
return mime_type == "application/http-exchange+cbor";
}
WebPackageRequestHandler::WebPackageRequestHandler() : weak_factory_(this) {
WebPackageRequestHandler::WebPackageRequestHandler(
url::Origin request_initiator,
scoped_refptr<SharedURLLoaderFactory> url_loader_factory,
URLLoaderThrottlesGetter url_loader_throttles_getter)
: request_initiator_(std::move(request_initiator)),
url_loader_factory_(url_loader_factory),
url_loader_throttles_getter_(std::move(url_loader_throttles_getter)),
weak_factory_(this) {
DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange));
}
......@@ -67,7 +75,9 @@ bool WebPackageRequestHandler::MaybeCreateLoaderForResponse(
// or reusing the existing ThrottlingURLLoader by reattaching URLLoaderClient,
// to support SafeBrowsing checking of the content of the WebPackage.
web_package_loader_ = std::make_unique<WebPackageLoader>(
response, std::move(client), url_loader->Unbind());
response, std::move(client), url_loader->Unbind(),
std::move(request_initiator_), std::move(url_loader_factory_),
std::move(url_loader_throttles_getter_));
return true;
}
......
......@@ -7,16 +7,26 @@
#include "base/memory/weak_ptr.h"
#include "content/browser/loader/url_loader_request_handler.h"
#include "content/public/common/resource_type.h"
#include "url/origin.h"
namespace content {
class SharedURLLoaderFactory;
class URLLoaderThrottle;
class WebPackageLoader;
class WebPackageRequestHandler final : public URLLoaderRequestHandler {
public:
using URLLoaderThrottlesGetter = base::RepeatingCallback<
std::vector<std::unique_ptr<content::URLLoaderThrottle>>()>;
static bool IsSupportedMimeType(const std::string& mime_type);
WebPackageRequestHandler();
WebPackageRequestHandler(
url::Origin request_initiator,
scoped_refptr<SharedURLLoaderFactory> url_loader_factory,
URLLoaderThrottlesGetter url_loader_throttles_getter);
~WebPackageRequestHandler() override;
// URLLoaderRequestHandler implementation
......@@ -38,6 +48,10 @@ class WebPackageRequestHandler final : public URLLoaderRequestHandler {
// StartResponse.
std::unique_ptr<WebPackageLoader> web_package_loader_;
url::Origin request_initiator_;
scoped_refptr<SharedURLLoaderFactory> url_loader_factory_;
URLLoaderThrottlesGetter url_loader_throttles_getter_;
base::WeakPtrFactory<WebPackageRequestHandler> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(WebPackageRequestHandler);
......
......@@ -2,17 +2,64 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/content_features.h"
#include "content/public/common/page_type.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_navigation_throttle.h"
#include "content/public/test/url_loader_interceptor.h"
#include "content/shell/browser/shell.h"
#include "net/test/test_data_directory.h"
#include "net/test/url_request/url_request_mock_http_job.h"
#include "net/url_request/url_request_filter.h"
#include "net/url_request/url_request_interceptor.h"
#include "services/network/public/cpp/features.h"
namespace content {
namespace {
const char* kMockHeaderFileSuffix = ".mock-http-headers";
class NavigationFailureObserver : public WebContentsObserver {
public:
explicit NavigationFailureObserver(WebContents* web_contents)
: WebContentsObserver(web_contents) {}
~NavigationFailureObserver() override = default;
void DidStartNavigation(content::NavigationHandle* handle) override {
auto throttle = std::make_unique<TestNavigationThrottle>(handle);
throttle->SetCallback(
TestNavigationThrottle::WILL_FAIL_REQUEST,
base::BindRepeating(&NavigationFailureObserver::OnWillFailRequestCalled,
base::Unretained(this)));
handle->RegisterThrottleForTesting(
std::unique_ptr<TestNavigationThrottle>(std::move(throttle)));
}
bool did_fail() const { return did_fail_; }
private:
void OnWillFailRequestCalled() { did_fail_ = true; }
bool did_fail_ = false;
DISALLOW_COPY_AND_ASSIGN(NavigationFailureObserver);
};
} // namespace
class WebPackageRequestHandlerBrowserTest
: public ContentBrowserTest,
public testing::WithParamInterface<bool> {
......@@ -31,15 +78,81 @@ class WebPackageRequestHandlerBrowserTest
ContentBrowserTest::SetUp();
}
void TearDownOnMainThread() override { interceptor_.reset(); }
protected:
void InstallUrlInterceptor(const GURL& url, const std::string& data_path) {
if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
if (!interceptor_) {
interceptor_ =
std::make_unique<content::URLLoaderInterceptor>(base::BindRepeating(
&WebPackageRequestHandlerBrowserTest::OnInterceptCallback,
base::Unretained(this)));
}
interceptor_data_path_map_[url] = data_path;
} else {
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::BindOnce(&InstallMockInterceptors, url, data_path));
}
}
private:
static std::string ReadFile(const std::string& data_path) {
base::ScopedAllowBlockingForTesting allow_io;
base::FilePath root_path;
CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &root_path));
std::string contents;
CHECK(base::ReadFileToString(root_path.AppendASCII(data_path), &contents));
return contents;
}
static std::string ReadHeaderFile(const std::string& data_path) {
std::string header_file_relative_path = data_path + kMockHeaderFileSuffix;
base::ScopedAllowBlockingForTesting allow_io;
base::FilePath root_path;
CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &root_path));
if (!base::PathExists(root_path.AppendASCII(header_file_relative_path)))
return "HTTP/1.0 200 OK\n";
return ReadFile(header_file_relative_path);
}
static void InstallMockInterceptors(const GURL& url,
const std::string& data_path) {
DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService));
base::FilePath root_path;
CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &root_path));
net::URLRequestFilter::GetInstance()->AddUrlInterceptor(
url, net::URLRequestMockHTTPJob::CreateInterceptorForSingleFile(
root_path.AppendASCII(data_path)));
}
bool OnInterceptCallback(
content::URLLoaderInterceptor::RequestParams* params) {
DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService));
const auto it = interceptor_data_path_map_.find(params->url_request.url);
if (it == interceptor_data_path_map_.end())
return false;
content::URLLoaderInterceptor::WriteResponse(
ReadHeaderFile(it->second), ReadFile(it->second), params->client.get());
return true;
}
bool is_network_service_enabled() const { return GetParam(); }
base::test::ScopedFeatureList feature_list_;
std::unique_ptr<content::URLLoaderInterceptor> interceptor_;
std::map<GURL, std::string> interceptor_data_path_map_;
DISALLOW_COPY_AND_ASSIGN(WebPackageRequestHandlerBrowserTest);
};
IN_PROC_BROWSER_TEST_P(WebPackageRequestHandlerBrowserTest, Simple) {
InstallUrlInterceptor(
GURL("https://cert.example.org/cert.msg"),
"content/test/data/htxg/wildcard_example.org.public.pem.msg");
embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL("/htxg/test.example.org_test.htxg");
......@@ -49,6 +162,22 @@ IN_PROC_BROWSER_TEST_P(WebPackageRequestHandlerBrowserTest, Simple) {
EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
}
IN_PROC_BROWSER_TEST_P(WebPackageRequestHandlerBrowserTest, CertNotFound) {
InstallUrlInterceptor(GURL("https://cert.example.org/cert.msg"),
"content/test/data/htxg/404.msg");
embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL("/htxg/test.example.org_test.htxg");
NavigationFailureObserver failure_observer(shell()->web_contents());
NavigateToURL(shell(), url);
EXPECT_TRUE(failure_observer.did_fail());
NavigationEntry* entry =
shell()->web_contents()->GetController().GetVisibleEntry();
EXPECT_EQ(content::PAGE_TYPE_ERROR, entry->GetPageType());
}
INSTANTIATE_TEST_CASE_P(WebPackageRequestHandlerBrowserTest,
WebPackageRequestHandlerBrowserTest,
testing::Bool());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment