Commit b8d512a4 authored by Tsuyoshi Horo's avatar Tsuyoshi Horo Committed by Commit Bot

Initial implementation of SignedExchangeHandler and WebPackage response handling.

This CL implements the following classes:
 - SignedExchangeHandler
 - WebPackageLoader
 - WebPackageRequestHandler

But Currenly SignedExchangeHandler doesn't implement any CBOR parsing logic nor
verifying logic. It just behaves as if the passed body is a signed HTTP exchange
which contains a request to "https://example.com/test.html" and a response with
a payload which is equal to the original body.

The added LayoutTest "origin-signed-response.html" must pass only when
SignedHTTPExchange feature is enabled.

Bug: 803774

Change-Id: I53d397d7cdfe79d48d6891db65ab1a1e12100b92
Reviewed-on: https://chromium-review.googlesource.com/869872
Commit-Queue: Tsuyoshi Horo <horo@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarKouhei Ueno <kouhei@chromium.org>
Cr-Commit-Position: refs/heads/master@{#531921}
parent 33d0f6fa
...@@ -1012,6 +1012,8 @@ jumbo_source_set("browser") { ...@@ -1012,6 +1012,8 @@ jumbo_source_set("browser") {
"loader/resource_requester_info.h", "loader/resource_requester_info.h",
"loader/resource_scheduler_filter.cc", "loader/resource_scheduler_filter.cc",
"loader/resource_scheduler_filter.h", "loader/resource_scheduler_filter.h",
"loader/signed_exchange_handler.cc",
"loader/signed_exchange_handler.h",
"loader/stream_resource_handler.cc", "loader/stream_resource_handler.cc",
"loader/stream_resource_handler.h", "loader/stream_resource_handler.h",
"loader/stream_writer.cc", "loader/stream_writer.cc",
...@@ -1028,6 +1030,10 @@ jumbo_source_set("browser") { ...@@ -1028,6 +1030,10 @@ jumbo_source_set("browser") {
"loader/url_loader_request_handler.h", "loader/url_loader_request_handler.h",
"loader/wake_lock_resource_throttle.cc", "loader/wake_lock_resource_throttle.cc",
"loader/wake_lock_resource_throttle.h", "loader/wake_lock_resource_throttle.h",
"loader/web_package_loader.cc",
"loader/web_package_loader.h",
"loader/web_package_request_handler.cc",
"loader/web_package_request_handler.h",
"loader_delegate_impl.cc", "loader_delegate_impl.cc",
"loader_delegate_impl.h", "loader_delegate_impl.h",
"locks/lock_manager.cc", "locks/lock_manager.cc",
......
...@@ -600,7 +600,8 @@ void AppCacheRequestHandler::MaybeCreateLoader( ...@@ -600,7 +600,8 @@ void AppCacheRequestHandler::MaybeCreateLoader(
bool AppCacheRequestHandler::MaybeCreateLoaderForResponse( bool AppCacheRequestHandler::MaybeCreateLoaderForResponse(
const network::ResourceResponseHead& response, const network::ResourceResponseHead& response,
network::mojom::URLLoaderPtr* loader, network::mojom::URLLoaderPtr* loader,
network::mojom::URLLoaderClientRequest* client_request) { network::mojom::URLLoaderClientRequest* client_request,
ThrottlingURLLoader* url_loader) {
// The sync interface of this method is inherited from the // The sync interface of this method is inherited from the
// URLLoaderRequestHandler class. The LoaderCallback created here is invoked // URLLoaderRequestHandler class. The LoaderCallback created here is invoked
// synchronously in fallback cases, and only when there really is a loader // synchronously in fallback cases, and only when there really is a loader
......
...@@ -93,7 +93,8 @@ class CONTENT_EXPORT AppCacheRequestHandler ...@@ -93,7 +93,8 @@ class CONTENT_EXPORT AppCacheRequestHandler
bool MaybeCreateLoaderForResponse( bool MaybeCreateLoaderForResponse(
const network::ResourceResponseHead& response, const network::ResourceResponseHead& response,
network::mojom::URLLoaderPtr* loader, network::mojom::URLLoaderPtr* loader,
network::mojom::URLLoaderClientRequest* client_request) override; network::mojom::URLLoaderClientRequest* client_request,
ThrottlingURLLoader* url_loader) override;
base::Optional<SubresourceLoaderParams> MaybeCreateSubresourceLoaderParams() base::Optional<SubresourceLoaderParams> MaybeCreateSubresourceLoaderParams()
override; override;
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <vector> #include <vector>
#include "base/bind.h" #include "base/bind.h"
#include "base/feature_list.h"
#include "base/location.h" #include "base/location.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
...@@ -20,6 +21,7 @@ ...@@ -20,6 +21,7 @@
#include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/loader/resource_dispatcher_host_impl.h"
#include "content/browser/loader/resource_request_info_impl.h" #include "content/browser/loader/resource_request_info_impl.h"
#include "content/browser/loader/stream_resource_handler.h" #include "content/browser/loader/stream_resource_handler.h"
#include "content/browser/loader/web_package_request_handler.h"
#include "content/public/browser/content_browser_client.h" #include "content/public/browser/content_browser_client.h"
#include "content/public/browser/download_item.h" #include "content/public/browser/download_item.h"
#include "content/public/browser/download_save_info.h" #include "content/public/browser/download_save_info.h"
...@@ -27,6 +29,7 @@ ...@@ -27,6 +29,7 @@
#include "content/public/browser/plugin_service.h" #include "content/public/browser/plugin_service.h"
#include "content/public/browser/resource_context.h" #include "content/public/browser/resource_context.h"
#include "content/public/browser/resource_dispatcher_host_delegate.h" #include "content/public/browser/resource_dispatcher_host_delegate.h"
#include "content/public/common/content_features.h"
#include "content/public/common/webplugininfo.h" #include "content/public/common/webplugininfo.h"
#include "net/base/io_buffer.h" #include "net/base/io_buffer.h"
#include "net/base/mime_sniffer.h" #include "net/base/mime_sniffer.h"
...@@ -414,6 +417,10 @@ bool MimeSniffingResourceHandler::MaybeStartInterception() { ...@@ -414,6 +417,10 @@ bool MimeSniffingResourceHandler::MaybeStartInterception() {
if (!must_download) { if (!must_download) {
if (blink::IsSupportedMimeType(mime_type)) if (blink::IsSupportedMimeType(mime_type))
return true; return true;
if (base::FeatureList::IsEnabled(features::kSignedHTTPExchange) &&
WebPackageRequestHandler::IsSupportedMimeType(mime_type)) {
return true;
}
bool handled_by_plugin; bool handled_by_plugin;
if (!CheckForPluginHandler(&handled_by_plugin)) if (!CheckForPluginHandler(&handled_by_plugin))
......
...@@ -63,7 +63,8 @@ class TestURLLoaderRequestHandler : public URLLoaderRequestHandler { ...@@ -63,7 +63,8 @@ class TestURLLoaderRequestHandler : public URLLoaderRequestHandler {
bool MaybeCreateLoaderForResponse( bool MaybeCreateLoaderForResponse(
const network::ResourceResponseHead& response, const network::ResourceResponseHead& response,
network::mojom::URLLoaderPtr* loader, network::mojom::URLLoaderPtr* loader,
network::mojom::URLLoaderClientRequest* client_request) override { network::mojom::URLLoaderClientRequest* client_request,
ThrottlingURLLoader* url_loader) override {
return false; return false;
} }
......
// 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_handler.h"
#include "base/feature_list.h"
#include "content/public/common/content_features.h"
#include "mojo/public/cpp/system/string_data_pipe_producer.h"
#include "net/http/http_response_headers.h"
#include "services/network/public/cpp/resource_response.h"
#include "services/network/public/cpp/url_loader_completion_status.h"
namespace content {
namespace {
constexpr size_t kPipeSizeForSignedResponseBody = 65536;
} // namespace
SignedExchangeHandler::SignedExchangeHandler(
mojo::ScopedDataPipeConsumerHandle body)
: body_(std::move(body)) {
DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange));
}
SignedExchangeHandler::~SignedExchangeHandler() = default;
void SignedExchangeHandler::GetHTTPExchange(
ExchangeFoundCallback found_callback,
ExchangeFinishedCallback finish_callback) {
DCHECK(!found_callback_);
DCHECK(!finish_callback_);
found_callback_ = std::move(found_callback);
finish_callback_ = std::move(finish_callback);
drainer_.reset(new mojo::common::DataPipeDrainer(this, std::move(body_)));
}
void SignedExchangeHandler::OnDataAvailable(const void* data,
size_t num_bytes) {
original_body_string_.append(static_cast<const char*>(data), num_bytes);
}
void SignedExchangeHandler::OnDataComplete() {
if (!found_callback_)
return;
// TODO(https://crbug.com/80374): Get the request url by parsing CBOR format.
GURL request_url = GURL("https://example.com/test.html");
// TODO(https://crbug.com/80374): Get the request method by parsing CBOR
// format.
std::string request_method = "GET";
// TODO(https://crbug.com/80374): Get the payload by parsing CBOR format.
std::string payload = original_body_string_;
original_body_string_.clear();
// TODO(https://crbug.com/80374): Get more headers by parsing CBOR.
scoped_refptr<net::HttpResponseHeaders> headers(
new net::HttpResponseHeaders("HTTP/1.1 200 OK"));
network::ResourceResponseHead resource_response;
resource_response.headers = headers;
resource_response.mime_type = "text/html";
// TODO(https://crbug.com/80374): Get the certificate by parsing CBOR and
// verify.
base::Optional<net::SSLInfo> ssl_info;
mojo::DataPipe pipe(kPipeSizeForSignedResponseBody);
// TODO(https://crbug.com/80374): Write the error handling code. This could
// fail.
DCHECK(pipe.consumer_handle.is_valid());
mojo::ScopedDataPipeConsumerHandle response_body =
std::move(pipe.consumer_handle);
std::move(found_callback_)
.Run(request_url, request_method, resource_response, std::move(ssl_info),
std::move(response_body));
data_producer_ = std::make_unique<mojo::StringDataPipeProducer>(
std::move(pipe.producer_handle));
data_producer_->Write(payload,
base::BindOnce(&SignedExchangeHandler::OnDataWritten,
base::Unretained(this)));
}
void SignedExchangeHandler::OnDataWritten(MojoResult result) {
data_producer_.reset();
std::move(finish_callback_)
.Run(network::URLLoaderCompletionStatus(
result == MOJO_RESULT_OK ? net::OK : net::ERR_FAILED));
}
} // 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_HANDLER_H_
#define CONTENT_BROWSER_LOADER_SIGNED_EXCHANGE_HANDLER_H_
#include <string>
#include "base/callback.h"
#include "base/optional.h"
#include "mojo/common/data_pipe_drainer.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "net/ssl/ssl_info.h"
#include "url/gurl.h"
namespace mojo {
class StringDataPipeProducer;
} // namespace mojo
namespace network {
struct ResourceResponseHead;
struct URLLoaderCompletionStatus;
} // namespace network
namespace content {
// IMPORTANT: Currenly SignedExchangeHandler doesn't implement any CBOR parsing
// logic nor verifying logic. It just behaves as if the passed body is a signed
// HTTP exchange which contains a request to "https://example.com/test.html" and
// a response with a payload which is equal to the original body.
// TODO(https://crbug.com/80374): Implement CBOR parsing logic and verifying
// logic.
class SignedExchangeHandler final
: public mojo::common::DataPipeDrainer::Client {
public:
using ExchangeFoundCallback =
base::OnceCallback<void(const GURL& request_url,
const std::string& request_method,
const network::ResourceResponseHead&,
base::Optional<net::SSLInfo>,
mojo::ScopedDataPipeConsumerHandle)>;
using ExchangeFinishedCallback =
base::OnceCallback<void(const network::URLLoaderCompletionStatus&)>;
// The passed |body| will be read to parse the signed HTTP exchange.
// TODO(https://crbug.com/80374): Consider making SignedExchangeHandler
// independent from DataPipe to make it easy to port it in //net.
explicit SignedExchangeHandler(mojo::ScopedDataPipeConsumerHandle body);
~SignedExchangeHandler() override;
// TODO(https://crbug.com/80374): Currently SignedExchangeHandler always calls
// found_callback and then calls finish_callback after reading the all buffer.
// Need to redesign this callback model when we will introduce
// SignedExchangeHandler::Reader class to read the body and introduce the
// cert verification.
void GetHTTPExchange(ExchangeFoundCallback found_callback,
ExchangeFinishedCallback finish_callback);
private:
// mojo::Common::DataPipeDrainer::Client
void OnDataAvailable(const void* data, size_t num_bytes) override;
void OnDataComplete() override;
// Called from |data_producer_|.
void OnDataWritten(MojoResult result);
mojo::ScopedDataPipeConsumerHandle body_;
std::unique_ptr<mojo::common::DataPipeDrainer> drainer_;
ExchangeFoundCallback found_callback_;
ExchangeFinishedCallback finish_callback_;
std::string original_body_string_;
std::unique_ptr<mojo::StringDataPipeProducer> data_producer_;
DISALLOW_COPY_AND_ASSIGN(SignedExchangeHandler);
};
} // namespace content
#endif // CONTENT_BROWSER_LOADER_SIGNED_EXCHANGE_HANDLER_H_
...@@ -16,7 +16,8 @@ URLLoaderRequestHandler::MaybeCreateSubresourceLoaderParams() { ...@@ -16,7 +16,8 @@ URLLoaderRequestHandler::MaybeCreateSubresourceLoaderParams() {
bool URLLoaderRequestHandler::MaybeCreateLoaderForResponse( bool URLLoaderRequestHandler::MaybeCreateLoaderForResponse(
const network::ResourceResponseHead& response, const network::ResourceResponseHead& response,
network::mojom::URLLoaderPtr* loader, network::mojom::URLLoaderPtr* loader,
network::mojom::URLLoaderClientRequest* client_request) { network::mojom::URLLoaderClientRequest* client_request,
ThrottlingURLLoader* url_loader) {
return false; return false;
} }
......
...@@ -19,6 +19,7 @@ namespace content { ...@@ -19,6 +19,7 @@ namespace content {
class ResourceContext; class ResourceContext;
struct ResourceRequest; struct ResourceRequest;
struct SubresourceLoaderParams; struct SubresourceLoaderParams;
class ThrottlingURLLoader;
using StartLoaderCallback = using StartLoaderCallback =
base::OnceCallback<void(network::mojom::URLLoaderRequest request, base::OnceCallback<void(network::mojom::URLLoaderRequest request,
...@@ -56,10 +57,17 @@ class CONTENT_EXPORT URLLoaderRequestHandler { ...@@ -56,10 +57,17 @@ class CONTENT_EXPORT URLLoaderRequestHandler {
// The URLLoader interface pointer is returned in the |loader| parameter. // The URLLoader interface pointer is returned in the |loader| parameter.
// The interface request for the URLLoaderClient is returned in the // The interface request for the URLLoaderClient is returned in the
// |client_request| parameter. // |client_request| parameter.
// The |url_loader| points to the ThrottlingURLLoader that currently controls
// the request. It can be optionally consumed to get the current
// URLLoaderClient and URLLoader so that the implementation can rebind them to
// intercept the inflight loading if necessary. Note that the |url_loader|
// will be reset after this method is called, which will also drop the
// URLLoader held by |url_loader_| if it is not unbound yet.
virtual bool MaybeCreateLoaderForResponse( virtual bool MaybeCreateLoaderForResponse(
const network::ResourceResponseHead& response, const network::ResourceResponseHead& response,
network::mojom::URLLoaderPtr* loader, network::mojom::URLLoaderPtr* loader,
network::mojom::URLLoaderClientRequest* client_request); network::mojom::URLLoaderClientRequest* client_request,
ThrottlingURLLoader* url_loader);
}; };
} // namespace content } // 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/web_package_loader.h"
#include "base/feature_list.h"
#include "base/strings/stringprintf.h"
#include "content/browser/loader/signed_exchange_handler.h"
#include "content/public/common/content_features.h"
#include "net/http/http_util.h"
namespace content {
namespace {
net::RedirectInfo CreateRedirectInfo(const GURL& new_url) {
net::RedirectInfo redirect_info;
redirect_info.new_url = new_url;
redirect_info.new_method = "GET";
redirect_info.status_code = 302;
redirect_info.new_site_for_cookies = redirect_info.new_url;
return redirect_info;
}
} // namespace
class WebPackageLoader::ResponseTimingInfo {
public:
explicit ResponseTimingInfo(const network::ResourceResponseHead& response)
: request_start_(response.request_start),
response_start_(response.response_start),
request_time_(response.request_time),
response_time_(response.response_time),
load_timing_(response.load_timing) {}
network::ResourceResponseHead CreateRedirectResponseHead() const {
network::ResourceResponseHead response_head;
response_head.encoded_data_length = 0;
std::string buf(base::StringPrintf("HTTP/1.1 %d %s\r\n", 302, "Found"));
response_head.headers = new net::HttpResponseHeaders(
net::HttpUtil::AssembleRawHeaders(buf.c_str(), buf.size()));
response_head.encoded_data_length = 0;
response_head.request_start = request_start_;
response_head.response_start = response_start_;
response_head.request_time = request_time_;
response_head.response_time = response_time_;
response_head.load_timing = load_timing_;
return response_head;
}
private:
const base::TimeTicks request_start_;
const base::TimeTicks response_start_;
const base::Time request_time_;
const base::Time response_time_;
const net::LoadTimingInfo load_timing_;
DISALLOW_COPY_AND_ASSIGN(ResponseTimingInfo);
};
WebPackageLoader::WebPackageLoader(
const network::ResourceResponseHead& original_response,
network::mojom::URLLoaderClientPtr forwarding_client,
network::mojom::URLLoaderClientEndpointsPtr endpoints)
: original_response_timing_info_(
base::MakeUnique<ResponseTimingInfo>(original_response)),
forwarding_client_(std::move(forwarding_client)),
url_loader_client_binding_(this),
weak_factory_(this) {
DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange));
url_loader_.Bind(std::move(endpoints->url_loader));
if (!base::FeatureList::IsEnabled(features::kNetworkService)) {
// We don't propagate the response to the navigation request and its
// throttles, therefore we need to call this here internally in order to
// move it forward.
// TODO(https://crbug.com/791049): Remove this when NetworkService is
// enabled by default.
url_loader_->ProceedWithResponse();
}
// Bind the endpoint with |this| to get the body DataPipe.
url_loader_client_binding_.Bind(std::move(endpoints->url_loader_client));
// |client_| will be bound with a forwarding client by ConnectToClient().
pending_client_request_ = mojo::MakeRequest(&client_);
}
WebPackageLoader::~WebPackageLoader() = default;
void WebPackageLoader::OnReceiveResponse(
const network::ResourceResponseHead& response_head,
const base::Optional<net::SSLInfo>& ssl_info,
network::mojom::DownloadedTempFilePtr downloaded_file) {
// Must not be called because this WebPackageLoader and the client endpoints
// were bound after OnReceiveResponse() is called.
NOTREACHED();
}
void WebPackageLoader::OnReceiveRedirect(
const net::RedirectInfo& redirect_info,
const network::ResourceResponseHead& response_head) {
// Must not be called because this WebPackageLoader and the client endpoints
// were bound after OnReceiveResponse() is called.
NOTREACHED();
}
void WebPackageLoader::OnDataDownloaded(int64_t data_len,
int64_t encoded_data_len) {
// Must not be called because this WebPackageLoader and the client endpoints
// were bound after OnReceiveResponse() is called.
NOTREACHED();
}
void WebPackageLoader::OnUploadProgress(int64_t current_position,
int64_t total_size,
OnUploadProgressCallback ack_callback) {
// Must not be called because this WebPackageLoader and the client endpoints
// were bound after OnReceiveResponse() is called.
NOTREACHED();
}
void WebPackageLoader::OnReceiveCachedMetadata(
const std::vector<uint8_t>& data) {
// Curerntly CachedMetadata for WebPackage is not supported.
NOTREACHED();
}
void WebPackageLoader::OnTransferSizeUpdated(int32_t transfer_size_diff) {
// TODO(https://crbug.com/80374): Implement this to progressively update the
// encoded data length in DevTools.
}
void WebPackageLoader::OnStartLoadingResponseBody(
mojo::ScopedDataPipeConsumerHandle body) {
signed_exchange_handler_ =
base::MakeUnique<SignedExchangeHandler>(std::move(body));
signed_exchange_handler_->GetHTTPExchange(
base::BindOnce(&WebPackageLoader::OnHTTPExchangeFound,
weak_factory_.GetWeakPtr()),
base::BindOnce(&WebPackageLoader::OnHTTPExchangeFinished,
weak_factory_.GetWeakPtr()));
}
void WebPackageLoader::OnComplete(
const network::URLLoaderCompletionStatus& status) {
// TODO(https://crbug.com/80374): Copy the data length information and pass to
// |client_| when OnHTTPExchangeFinished() is called.
}
void WebPackageLoader::FollowRedirect() {
NOTREACHED();
}
void WebPackageLoader::ProceedWithResponse() {
// TODO(https://crbug.com/791049): Remove this when NetworkService is
// enabled by default.
DCHECK(!base::FeatureList::IsEnabled(features::kNetworkService));
DCHECK(pending_body_.is_valid());
DCHECK(client_);
client_->OnStartLoadingResponseBody(std::move(pending_body_));
if (pending_completion_status_) {
client_->OnComplete(*pending_completion_status_);
}
}
void WebPackageLoader::SetPriority(net::RequestPriority priority,
int intra_priority_value) {
// TODO(https://crbug.com/80374): Implement this.
}
void WebPackageLoader::PauseReadingBodyFromNet() {
// TODO(https://crbug.com/80374): Implement this.
}
void WebPackageLoader::ResumeReadingBodyFromNet() {
// TODO(https://crbug.com/80374): Implement this.
}
void WebPackageLoader::ConnectToClient(
network::mojom::URLLoaderClientPtr client) {
DCHECK(pending_client_request_.is_pending());
mojo::FuseInterface(std::move(pending_client_request_),
client.PassInterface());
}
void WebPackageLoader::OnHTTPExchangeFound(
const GURL& request_url,
const std::string& request_method,
const network::ResourceResponseHead& resource_response,
base::Optional<net::SSLInfo> ssl_info,
mojo::ScopedDataPipeConsumerHandle body) {
// TODO(https://crbug.com/80374): Handle no-GET request_method as a error.
DCHECK(original_response_timing_info_);
forwarding_client_->OnReceiveRedirect(
CreateRedirectInfo(request_url),
std::move(original_response_timing_info_)->CreateRedirectResponseHead());
forwarding_client_->OnComplete(network::URLLoaderCompletionStatus(net::OK));
forwarding_client_.reset();
client_->OnReceiveResponse(resource_response, std::move(ssl_info),
nullptr /* downloaded_file */);
if (!base::FeatureList::IsEnabled(features::kNetworkService)) {
// Need to wait until ProceedWithResponse() is called.
pending_body_ = std::move(body);
} else {
client_->OnStartLoadingResponseBody(std::move(body));
}
}
void WebPackageLoader::OnHTTPExchangeFinished(
const network::URLLoaderCompletionStatus& status) {
if (pending_body_.is_valid()) {
DCHECK(!base::FeatureList::IsEnabled(features::kNetworkService));
// If ProceedWithResponse() was not called yet, need to call OnComplete()
// after ProceedWithResponse() is called.
pending_completion_status_ = status;
} else {
client_->OnComplete(status);
}
}
} // 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_WEB_PACKAGE_LOADER_H_
#define CONTENT_BROWSER_LOADER_WEB_PACKAGE_LOADER_H_
#include "base/optional.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "net/ssl/ssl_info.h"
#include "net/url_request/redirect_info.h"
#include "services/network/public/interfaces/url_loader.mojom.h"
namespace content {
class SignedExchangeHandler;
// WebPackageLoader handles an origin-signed HTTP exchange response. It is
// created when a WebPackageRequestHandler recieves an origin-signed HTTP
// exchange response, and is owned by the handler until the StartLoaderCallback
// of WebPackageRequestHandler::StartResponse is called. After that, it is
// owned by the URLLoader mojo endpoint.
class WebPackageLoader final : public network::mojom::URLLoaderClient,
public network::mojom::URLLoader {
public:
WebPackageLoader(const network::ResourceResponseHead& original_response,
network::mojom::URLLoaderClientPtr forwarding_client,
network::mojom::URLLoaderClientEndpointsPtr endpoints);
~WebPackageLoader() override;
// network::mojom::URLLoaderClient implementation
// Only OnStartLoadingResponseBody() and OnComplete() are called.
void OnReceiveResponse(
const network::ResourceResponseHead& response_head,
const base::Optional<net::SSLInfo>& ssl_info,
network::mojom::DownloadedTempFilePtr downloaded_file) override;
void OnReceiveRedirect(
const net::RedirectInfo& redirect_info,
const network::ResourceResponseHead& response_head) override;
void OnDataDownloaded(int64_t data_len, int64_t encoded_data_len) override;
void OnUploadProgress(int64_t current_position,
int64_t total_size,
OnUploadProgressCallback ack_callback) override;
void OnReceiveCachedMetadata(const std::vector<uint8_t>& data) override;
void OnTransferSizeUpdated(int32_t transfer_size_diff) override;
void OnStartLoadingResponseBody(
mojo::ScopedDataPipeConsumerHandle body) override;
void OnComplete(const network::URLLoaderCompletionStatus& status) override;
// network::mojom::URLLoader implementation
void FollowRedirect() override;
void ProceedWithResponse() override;
void SetPriority(net::RequestPriority priority,
int intra_priority_value) override;
void PauseReadingBodyFromNet() override;
void ResumeReadingBodyFromNet() override;
void ConnectToClient(network::mojom::URLLoaderClientPtr client);
private:
class ResponseTimingInfo;
// Called from |signed_exchange_handler_| when it finds an origin-signed HTTP
// exchange.
void OnHTTPExchangeFound(
const GURL& request_url,
const std::string& request_method,
const network::ResourceResponseHead& resource_response,
base::Optional<net::SSLInfo> ssl_info,
mojo::ScopedDataPipeConsumerHandle body);
// Called from |signed_exchange_handler_| when it finished sending the
// payload of the origin-signed HTTP response.
void OnHTTPExchangeFinished(const network::URLLoaderCompletionStatus& status);
// This timing info is used to create a dummy redirect response.
std::unique_ptr<const ResponseTimingInfo> original_response_timing_info_;
// This client is alive until OnHTTPExchangeFound() is called.
network::mojom::URLLoaderClientPtr forwarding_client_;
// This |url_loader_| is the pointer of the network URL loader.
network::mojom::URLLoaderPtr url_loader_;
// This binding connects |this| with the network URL loader.
mojo::Binding<network::mojom::URLLoaderClient> url_loader_client_binding_;
// This is pending until connected by ConnectToClient().
network::mojom::URLLoaderClientPtr client_;
// This URLLoaderClientRequest is used by ConnectToClient() to connect
// |client_|.
network::mojom::URLLoaderClientRequest pending_client_request_;
std::unique_ptr<SignedExchangeHandler> signed_exchange_handler_;
// This is used to keep the DataPipe until ProceedWithResponse() is called.
mojo::ScopedDataPipeConsumerHandle pending_body_;
// This is used to keep the status until ProceedWithResponse() is called.
base::Optional<network::URLLoaderCompletionStatus> pending_completion_status_;
base::WeakPtrFactory<WebPackageLoader> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(WebPackageLoader);
};
} // namespace content
#endif // CONTENT_BROWSER_LOADER_WEB_PACKAGE_LOADER_H_
// Copyright 2017 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/web_package_request_handler.h"
#include "base/bind.h"
#include "base/feature_list.h"
#include "content/browser/loader/web_package_loader.h"
#include "content/common/throttling_url_loader.h"
#include "content/public/common/content_features.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "net/http/http_response_headers.h"
#include "services/network/public/cpp/resource_response.h"
#include "services/network/public/interfaces/url_loader.mojom.h"
namespace content {
// static
bool WebPackageRequestHandler::IsSupportedMimeType(
const std::string& mime_type) {
DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange));
return mime_type == "application/http-exchange+cbor";
}
WebPackageRequestHandler::WebPackageRequestHandler() : weak_factory_(this) {
DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange));
}
WebPackageRequestHandler::~WebPackageRequestHandler() = default;
void WebPackageRequestHandler::MaybeCreateLoader(
const network::ResourceRequest& resource_request,
ResourceContext* resource_context,
LoaderCallback callback) {
// TODO(https://crbug.com/80374): Ask WebPackageFetchManager to get the
// ongoing matching SignedExchangeHandler which was created by a
// WebPackagePrefetcher.
if (!web_package_loader_) {
std::move(callback).Run(StartLoaderCallback());
return;
}
std::move(callback).Run(base::BindOnce(
&WebPackageRequestHandler::StartResponse, weak_factory_.GetWeakPtr()));
}
bool WebPackageRequestHandler::MaybeCreateLoaderForResponse(
const network::ResourceResponseHead& response,
network::mojom::URLLoaderPtr* loader,
network::mojom::URLLoaderClientRequest* client_request,
ThrottlingURLLoader* url_loader) {
std::string mime_type;
if (response.was_fetched_via_service_worker || !response.headers ||
!response.headers->GetMimeType(&mime_type) ||
!IsSupportedMimeType(mime_type)) {
return false;
}
network::mojom::URLLoaderClientPtr client;
*client_request = mojo::MakeRequest(&client);
// TODO(https://crbug.com/80374): Consider creating a new ThrottlingURLLoader
// or reusing the existing ThrottlingURLLoader by reattaching URLLoaderClient,
// to support SafeBrowsing checking of the content of the WebPackage.
web_package_loader_ = base::MakeUnique<WebPackageLoader>(
response, std::move(client), url_loader->Unbind());
return true;
}
void WebPackageRequestHandler::StartResponse(
network::mojom::URLLoaderRequest request,
network::mojom::URLLoaderClientPtr client) {
web_package_loader_->ConnectToClient(std::move(client));
mojo::MakeStrongBinding(std::move(web_package_loader_), std::move(request));
}
} // 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_WEB_PACKAGE_REQUEST_HANDLER_H_
#define CONTENT_BROWSER_LOADER_WEB_PACKAGE_REQUEST_HANDLER_H_
#include "base/memory/weak_ptr.h"
#include "content/browser/loader/url_loader_request_handler.h"
namespace content {
class WebPackageLoader;
class WebPackageRequestHandler final : public URLLoaderRequestHandler {
public:
static bool IsSupportedMimeType(const std::string& mime_type);
WebPackageRequestHandler();
~WebPackageRequestHandler() override;
// URLLoaderRequestHandler implementation
void MaybeCreateLoader(const network::ResourceRequest& resource_request,
ResourceContext* resource_context,
LoaderCallback callback) override;
bool MaybeCreateLoaderForResponse(
const network::ResourceResponseHead& response,
network::mojom::URLLoaderPtr* loader,
network::mojom::URLLoaderClientRequest* client_request,
ThrottlingURLLoader* url_loader) override;
private:
void StartResponse(network::mojom::URLLoaderRequest request,
network::mojom::URLLoaderClientPtr client);
// Valid after MaybeCreateLoaderForResponse intercepts the request and until
// the loader is re-bound to the new client for the redirected request in
// StartResponse.
std::unique_ptr<WebPackageLoader> web_package_loader_;
base::WeakPtrFactory<WebPackageRequestHandler> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(WebPackageRequestHandler);
};
} // namespace content
#endif // CONTENT_BROWSER_LOADER_WEB_PACKAGE_REQUEST_HANDLER_H_
...@@ -22,7 +22,8 @@ bool IsNavigationMojoResponseEnabled() { ...@@ -22,7 +22,8 @@ bool IsNavigationMojoResponseEnabled() {
if (!IsBrowserSideNavigationEnabled()) if (!IsBrowserSideNavigationEnabled())
return false; return false;
return base::FeatureList::IsEnabled(features::kNavigationMojoResponse); return base::FeatureList::IsEnabled(features::kNavigationMojoResponse) ||
base::FeatureList::IsEnabled(features::kSignedHTTPExchange);
} }
} // namespace content } // namespace content
...@@ -96,6 +96,8 @@ crbug.com/805049 virtual/spv175/paint/invalidation/scroll/fixed-scroll-simple.ht ...@@ -96,6 +96,8 @@ crbug.com/805049 virtual/spv175/paint/invalidation/scroll/fixed-scroll-simple.ht
crbug.com/417782 virtual/spv175/compositing/overflow/border-radius-composited-subframe.html [ Failure ] crbug.com/417782 virtual/spv175/compositing/overflow/border-radius-composited-subframe.html [ Failure ]
crbug.com/417782 virtual/spv175/paint/invalidation/window-resize/window-resize-vertical-writing-mode.html [ Crash ] crbug.com/417782 virtual/spv175/paint/invalidation/window-resize/window-resize-vertical-writing-mode.html [ Crash ]
crbug.com/803774 http/tests/loading/htxg/ [ Skip ]
########## Ref tests can't be rebaselined ########## ########## Ref tests can't be rebaselined ##########
crbug.com/504613 crbug.com/524248 [ Mac ] paint/images/image-backgrounds-not-antialiased.html [ Failure ] crbug.com/504613 crbug.com/524248 [ Mac ] paint/images/image-backgrounds-not-antialiased.html [ Failure ]
......
...@@ -643,5 +643,15 @@ ...@@ -643,5 +643,15 @@
"prefix": "unified-autoplay", "prefix": "unified-autoplay",
"base": "external/wpt/feature-policy", "base": "external/wpt/feature-policy",
"args": ["--autoplay-policy=document-user-activation-required"] "args": ["--autoplay-policy=document-user-activation-required"]
},
{
"prefix": "htxg",
"base": "http/tests/loading/htxg/",
"args": ["--enable-features=SignedHTTPExchange"]
},
{
"prefix": "htxg-with-network-service",
"base": "http/tests/loading/htxg/",
"args": ["--enable-features=SignedHTTPExchange,NetworkService"]
} }
] ]
<!DOCTYPE html>
<title>Location of origin-signed HTTP response</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../../resources/get-host-info.js?pipe=sub"></script>
<body>
<script>
function with_iframe(url) {
return new Promise(function(resolve) {
const frame = document.createElement('iframe');
frame.src = url;
frame.onload = function() { resolve(frame); };
document.body.appendChild(frame);
});
}
promise_test(function(t) {
const url = 'resources/origin-signed-response-iframe.php';
return with_iframe(url)
.then((frame) => {
var channel = new MessageChannel();
var promise =
new Promise((resolve) => { channel.port1.onmessage = resolve; });
frame.contentWindow.postMessage(
{port: channel.port2}, '*', [channel.port2]);
return promise;
})
.then((event) => {
// TODO(https://crbug.com/80374): Currently this URL is hard coded in
// SignedExchangeHandler.
assert_equals(event.data.location, 'https://example.com/test.html');
});
}, 'Location of origin-signed HTTP response');
</script>
</body>
<?php
header('Content-Type: application/http-exchange+cbor');
?>
<!DOCTYPE html>
<body>
<script>
window.addEventListener('message', (event) => {
event.data.port.postMessage({location: document.location.href});
}, false);
</script>
hello<br>
world
</body>
This directory is for testing the SignedHTTPExchange feature with the Network
Service.
This directory is for testing the SignedHTTPExchange feature.
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