Commit 4ab8845f authored by Rouslan Solomakhin's avatar Rouslan Solomakhin Committed by Commit Bot

[Web Payment] Hosting manifest at payment method identifier URL.

Before this patch, the payment method manifest had to be hosted at a
location that is pointed to from the payment method identifier URL via
the HTTP Link rel=payment-method-manifest header. This complicated
testing in Chrome and prevented hosting payment method manifests on
popular hosting services, such as GitHub pages.

This patch falls back to returning the content of the page if it does
not have HTTP Link rel=payment-method-manifest header or that header is
not valid. The downloader uses HTTP GET instead of HTTP HEAD for the
payment method manifest, since the response body has to be considered.
An empty response body now generates an error earlier: in the downloader
instead of parser, because the parser always rejected empty input.

After this patch, it's possible to host the payment method manifest
directly at the URL of the payment method identifier, which makes
testing Chrome and developing payment apps easier.

Bug: 1035147
Change-Id: Id07c9023bac222ed45eecb8df91108f4df51e356
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2113735Reviewed-by: default avatarDanyao Wang <danyao@chromium.org>
Commit-Queue: Rouslan Solomakhin <rouslan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#753569}
parent faf7d2bb
......@@ -191,9 +191,8 @@ public class PaymentManifestDownloaderTest implements ManifestDownloadCallback {
Assert.assertTrue(
"Payment method manifest should have not have been downloaded.", mDownloadFailure);
Assert.assertEquals("Unable to make a HEAD request to \"" + uri.toString()
+ "\" for payment method manifest.",
mErrorMessage);
Assert.assertEquals(
"Unable to download payment manifest \"" + uri.toString() + "\".", mErrorMessage);
}
@Test
......
......@@ -533,8 +533,8 @@ IN_PROC_BROWSER_TEST_F(ManifestVerifierBrowserTest, ThreeTypesOfMethods) {
IN_PROC_BROWSER_TEST_F(ManifestVerifierBrowserTest,
SinglePaymentMethodName404) {
std::string expected_pattern =
"Unable to make a HEAD request to "
"\"https://127.0.0.1:\\d+/404.test/webpay\" for payment method manifest.";
"Unable to download payment manifest "
"\"https://127.0.0.1:\\d+/404.test/webpay\".";
{
content::PaymentAppProvider::PaymentApps apps;
apps[0] = std::make_unique<content::StoredPaymentApp>();
......@@ -572,9 +572,8 @@ IN_PROC_BROWSER_TEST_F(ManifestVerifierBrowserTest,
IN_PROC_BROWSER_TEST_F(ManifestVerifierBrowserTest,
MultiplePaymentMethodName404) {
std::string expected_pattern =
"Unable to make a HEAD request to "
"\"https://127.0.0.1:\\d+/404(aswell)?.test/webpay\" for payment method "
"manifest.";
"Unable to download payment manifest "
"\"https://127.0.0.1:\\d+/404(aswell)?.test/webpay\".";
{
content::PaymentAppProvider::PaymentApps apps;
apps[0] = std::make_unique<content::StoredPaymentApp>();
......
......@@ -53,9 +53,6 @@ const char kCrossOriginWebAppManifestNotAllowed[] =
const char kDetailedInvalidSslCertificateMessageFormat[] =
"SSL certificate is not valid. Security level: $.";
const char kHttpHeadRequestFailed[] =
"Unable to make a HEAD request to \"$1\" for payment method manifest.";
const char kHttpStatusCodeNotAllowed[] =
"HTTP status code $1 \"$2\" not allowed for payment method manifest "
"\"$3\".";
......@@ -89,9 +86,6 @@ const char kMissingMethodNameFromPaymentApp[] =
const char kMultiplePaymentMethodsNotSupportedFormat[] =
"The payment methods $ are not supported.";
const char kNoLinkRelPaymentMethodManifestHttpHeader[] =
"No \"Link: rel=payment-method-manifest\" HTTP header found at \"$1\".";
const char kNoResponseToPaymentEvent[] =
"Payment handler did not respond to \"paymentrequest\" event.";
......@@ -220,5 +214,12 @@ const char kCanMakePaymentEventNoExplicitlyVerifiedMethods[] =
const char kGenericPaymentMethodNotSupportedMessage[] =
"Payment method not supported.";
const char kNoContentAndNoLinkHeader[] =
"No content and no \"Link: rel=payment-method-manifest\" HTTP header found "
"at \"$1\".";
const char kNoContentInPaymentManifest[] =
"No content found in payment manifest \"$1\".";
} // namespace errors
} // namespace payments
......@@ -71,10 +71,6 @@ extern const char kCrossOriginWebAppManifestNotAllowed[];
// to replace.
extern const char kDetailedInvalidSslCertificateMessageFormat[];
// Used when a HEAD request for URL A fails. This format should be used with
// base::ReplaceStringPlaceholders(fmt, {A}, nullptr).
extern const char kHttpHeadRequestFailed[];
// Used for HTTP redirects that are prohibited for payment method manifests.
// This format should be used with base::ReplaceStringPlaceholders(fmt,
// {http_code, http_code_phrase, original_url}, nullptr).
......@@ -117,11 +113,6 @@ extern const char kMissingMethodNameFromPaymentApp[];
// where "$" is the character to replace.
extern const char kMultiplePaymentMethodsNotSupportedFormat[];
// Used when the payment method URL A does not have a "Link:
// rel=payment-method-manifest" HTTP header. This format should be used with
// base::ReplaceStringPlaceholders(fmt, {A}, nullptr).
extern const char kNoLinkRelPaymentMethodManifestHttpHeader[];
// Payment handler did not respond to the "paymentrequest" event.
extern const char kNoResponseToPaymentEvent[];
......@@ -250,6 +241,14 @@ extern const char kCanMakePaymentEventNoExplicitlyVerifiedMethods[];
// A message about unsupported payment method.
extern const char kGenericPaymentMethodNotSupportedMessage[];
// Used for errors downloading the payment method manifest. This format should
// be used with base::ReplaceStringPlaceholders(fmt, {A}, nullptr).
extern const char kNoContentAndNoLinkHeader[];
// User when the downloaded payment manifest A is empty. This format should be
// used with base::ReplaceStringPlaceholders(fmt, {A}, nullptr).
extern const char kNoContentInPaymentManifest[];
} // namespace errors
} // namespace payments
......
......@@ -12,6 +12,7 @@
#include "base/optional.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "components/link_header_util/link_header_util.h"
......@@ -33,59 +34,29 @@
namespace payments {
namespace {
GURL ParseResponseHeader(const GURL& url,
scoped_refptr<net::HttpResponseHeaders> headers,
const ErrorLogger& log,
std::string* out_error_message) {
if (!headers) {
*out_error_message = base::ReplaceStringPlaceholders(
errors::kNoLinkRelPaymentMethodManifestHttpHeader, {url.spec()},
nullptr);
log.Error(*out_error_message);
return GURL();
}
int response_code = headers->response_code();
if (response_code != net::HTTP_OK && response_code != net::HTTP_NO_CONTENT) {
*out_error_message = base::ReplaceStringPlaceholders(
errors::kHttpHeadRequestFailed, {url.spec()}, nullptr),
log.Error(*out_error_message);
return GURL();
}
std::string link_header;
headers->GetNormalizedHeader("link", &link_header);
if (link_header.empty()) {
*out_error_message = base::ReplaceStringPlaceholders(
errors::kNoLinkRelPaymentMethodManifestHttpHeader, {url.spec()},
nullptr);
log.Error(*out_error_message);
return GURL();
}
for (const auto& value : link_header_util::SplitLinkHeader(link_header)) {
std::string payment_method_manifest_url;
std::unordered_map<std::string, base::Optional<std::string>> params;
if (!link_header_util::ParseLinkHeaderValue(
value.first, value.second, &payment_method_manifest_url, &params)) {
continue;
}
auto rel = params.find("rel");
if (rel == params.end())
continue;
// Invokes |callback| with |error_format|.
void RespondWithError(const base::StringPiece& error_format,
const GURL& final_url,
const ErrorLogger& log,
PaymentManifestDownloadCallback callback) {
std::string error_message = base::ReplaceStringPlaceholders(
error_format, {final_url.spec()}, nullptr);
log.Error(error_message);
std::move(callback).Run(final_url, std::string(), error_message);
}
std::vector<std::string> rel_parts =
base::SplitString(rel->second.value_or(""), HTTP_LWS,
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
if (base::Contains(rel_parts, "payment-method-manifest"))
return url.Resolve(payment_method_manifest_url);
// Invokes the |callback| with |response_body|. If |response_body| is empty,
// then invokes |callback| with |empty_error_format|.
void RespondWithContent(const std::string& response_body,
const base::StringPiece& empty_error_format,
const GURL& final_url,
const ErrorLogger& log,
PaymentManifestDownloadCallback callback) {
if (response_body.empty()) {
RespondWithError(empty_error_format, final_url, log, std::move(callback));
} else {
std::move(callback).Run(final_url, response_body, std::string());
}
*out_error_message = base::ReplaceStringPlaceholders(
errors::kNoLinkRelPaymentMethodManifestHttpHeader, {url.spec()}, nullptr);
log.Error(*out_error_message);
return GURL();
}
bool IsValidManifestUrl(const GURL& url,
......@@ -126,22 +97,6 @@ GURL ParseRedirectUrl(const net::RedirectInfo& redirect_info,
return redirect_info.new_url;
}
std::string ParseResponseContent(
const GURL& final_url,
const std::string& response_body,
scoped_refptr<net::HttpResponseHeaders> headers,
const ErrorLogger& log,
std::string* out_error_message) {
if (!headers || headers->response_code() != net::HTTP_OK) {
*out_error_message = base::ReplaceStringPlaceholders(
errors::kPaymentManifestDownloadFailed, {final_url.spec()}, nullptr);
log.Error(*out_error_message);
return std::string();
}
return response_body;
}
} // namespace
PaymentManifestDownloader::PaymentManifestDownloader(
......@@ -160,7 +115,8 @@ void PaymentManifestDownloader::DownloadPaymentMethodManifest(
PaymentManifestDownloadCallback callback) {
DCHECK(UrlUtil::IsValidManifestUrl(url));
// Restrict number of redirects for efficiency and breaking circle.
InitiateDownload(merchant_origin, url, "HEAD",
InitiateDownload(merchant_origin, url,
Download::Type::RESPONSE_BODY_OR_LINK_HEADER,
/*allowed_number_of_redirects=*/3, std::move(callback));
}
......@@ -169,7 +125,8 @@ void PaymentManifestDownloader::DownloadWebAppManifest(
const GURL& url,
PaymentManifestDownloadCallback callback) {
DCHECK(UrlUtil::IsValidManifestUrl(url));
InitiateDownload(payment_method_manifest_origin, url, "GET",
InitiateDownload(payment_method_manifest_origin, url,
Download::Type::RESPONSE_BODY,
/*allowed_number_of_redirects=*/0, std::move(callback));
}
......@@ -195,7 +152,7 @@ void PaymentManifestDownloader::OnURLLoaderRedirect(
// Manually follow some type of redirects.
std::string error_message;
if (download->allowed_number_of_redirects > 0) {
DCHECK(download->method == "HEAD");
DCHECK_EQ(Download::Type::RESPONSE_BODY_OR_LINK_HEADER, download->type);
GURL redirect_url = ParseRedirectUrl(redirect_info, download->original_url,
*log_, &error_message);
if (!redirect_url.is_empty()) {
......@@ -204,7 +161,8 @@ void PaymentManifestDownloader::OnURLLoaderRedirect(
download->original_url, redirect_url,
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
// Redirects preserve the original request initiator.
InitiateDownload(download->request_initiator, redirect_url, "HEAD",
InitiateDownload(download->request_initiator, redirect_url,
Download::Type::RESPONSE_BODY_OR_LINK_HEADER,
--download->allowed_number_of_redirects,
std::move(download->callback));
return;
......@@ -250,41 +208,88 @@ void PaymentManifestDownloader::OnURLLoaderCompleteInternal(
downloads_.erase(download_it);
std::string error_message;
if (download->method == "GET") {
std::string content = ParseResponseContent(final_url, response_body,
headers, *log_, &error_message);
std::move(download->callback).Run(final_url, content, error_message);
if (download->type == Download::Type::RESPONSE_BODY) {
if (!headers || headers->response_code() != net::HTTP_OK) {
RespondWithError(errors::kPaymentManifestDownloadFailed, final_url, *log_,
std::move(download->callback));
} else {
RespondWithContent(response_body, errors::kNoContentInPaymentManifest,
final_url, *log_, std::move(download->callback));
}
return;
}
DCHECK_EQ("HEAD", download->method);
GURL payment_method_manifest_url =
ParseResponseHeader(final_url, headers, *log_, &error_message);
if (!payment_method_manifest_url.is_valid()) {
std::move(download->callback).Run(final_url, std::string(), error_message);
DCHECK_EQ(Download::Type::RESPONSE_BODY_OR_LINK_HEADER, download->type);
if (!headers) {
RespondWithContent(response_body, errors::kNoContentAndNoLinkHeader,
final_url, *log_, std::move(download->callback));
return;
}
if (!IsValidManifestUrl(payment_method_manifest_url, *log_, &error_message)) {
std::move(download->callback).Run(final_url, std::string(), error_message);
if (headers->response_code() != net::HTTP_OK &&
headers->response_code() != net::HTTP_NO_CONTENT) {
RespondWithError(errors::kPaymentManifestDownloadFailed, final_url, *log_,
std::move(download->callback));
return;
}
if (!url::IsSameOriginWith(final_url, payment_method_manifest_url)) {
error_message = base::ReplaceStringPlaceholders(
errors::kCrossOriginPaymentMethodManifestNotAllowed,
{payment_method_manifest_url.spec(), final_url.spec()}, nullptr);
log_->Error(error_message);
std::move(download->callback).Run(final_url, std::string(), error_message);
std::string link_header;
headers->GetNormalizedHeader("link", &link_header);
if (link_header.empty()) {
RespondWithContent(response_body, errors::kNoContentAndNoLinkHeader,
final_url, *log_, std::move(download->callback));
return;
}
// The request initiator for the payment method manifest is the origin of the
// HEAD request with the HTTP link header.
// https://github.com/w3c/webappsec-fetch-metadata/issues/30
InitiateDownload(
url::Origin::Create(final_url), payment_method_manifest_url, "GET",
/*allowed_number_of_redirects=*/0, std::move(download->callback));
for (const auto& value : link_header_util::SplitLinkHeader(link_header)) {
std::string link_url;
std::unordered_map<std::string, base::Optional<std::string>> params;
if (!link_header_util::ParseLinkHeaderValue(value.first, value.second,
&link_url, &params)) {
continue;
}
auto rel = params.find("rel");
if (rel == params.end())
continue;
std::vector<std::string> rel_parts =
base::SplitString(rel->second.value_or(""), HTTP_LWS,
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
if (base::Contains(rel_parts, "payment-method-manifest")) {
GURL payment_method_manifest_url = final_url.Resolve(link_url);
if (!IsValidManifestUrl(payment_method_manifest_url, *log_,
&error_message)) {
std::move(download->callback)
.Run(final_url, std::string(), error_message);
return;
}
if (!url::IsSameOriginWith(final_url, payment_method_manifest_url)) {
error_message = base::ReplaceStringPlaceholders(
errors::kCrossOriginPaymentMethodManifestNotAllowed,
{payment_method_manifest_url.spec(), final_url.spec()}, nullptr);
log_->Error(error_message);
std::move(download->callback)
.Run(final_url, std::string(), error_message);
return;
}
// The request initiator for the payment method manifest is the origin of
// the GET request with the HTTP link header.
// https://github.com/w3c/webappsec-fetch-metadata/issues/30
InitiateDownload(
url::Origin::Create(final_url), payment_method_manifest_url,
Download::Type::RESPONSE_BODY,
/*allowed_number_of_redirects=*/0, std::move(download->callback));
return;
}
}
RespondWithContent(response_body, errors::kNoContentAndNoLinkHeader,
final_url, *log_, std::move(download->callback));
}
network::SimpleURLLoader* PaymentManifestDownloader::GetLoaderForTesting() {
......@@ -300,11 +305,16 @@ GURL PaymentManifestDownloader::GetLoaderOriginalURLForTesting() {
void PaymentManifestDownloader::InitiateDownload(
const url::Origin& request_initiator,
const GURL& url,
const std::string& method,
Download::Type download_type,
int allowed_number_of_redirects,
PaymentManifestDownloadCallback callback) {
DCHECK(UrlUtil::IsValidManifestUrl(url));
// Only initial download of the payment method manifest (which might contain
// an HTTP Link header) is allowed to redirect.
DCHECK(allowed_number_of_redirects == 0 ||
download_type == Download::Type::RESPONSE_BODY_OR_LINK_HEADER);
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("payment_manifest_downloader", R"(
semantics {
......@@ -328,7 +338,7 @@ void PaymentManifestDownloader::InitiateDownload(
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->request_initiator = request_initiator;
resource_request->url = url;
resource_request->method = method;
resource_request->method = "GET";
resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
std::unique_ptr<network::SimpleURLLoader> loader =
network::SimpleURLLoader::Create(std::move(resource_request),
......@@ -343,7 +353,7 @@ void PaymentManifestDownloader::InitiateDownload(
auto download = std::make_unique<Download>();
download->request_initiator = request_initiator;
download->method = method;
download->type = download_type;
download->original_url = url;
download->loader = std::move(loader);
download->callback = std::move(callback);
......
......@@ -37,19 +37,24 @@ class ErrorLogger;
//
// Download failure results in empty contents. Failure to download the manifest
// can happen because of the following reasons:
// - HTTP response code is not 200. (204 is also allowed for HEAD request.)
// - HTTP GET on the manifest URL returns empty content.
// - HTTP response code is not 200. (204 is also allowed for payment method
// manifest.)
//
// In the case of a payment method manifest download, can also fail when:
// - More than three redirects.
// - Cross-site redirects.
// - HTTP response headers are absent.
// - HTTP response headers do not contain Link headers.
// - Link header does not contain rel="payment-method-manifest".
// - Link header does not contain a valid URL of the same origin.
// - HTTP GET on the manifest URL returns empty content and:
// - HTTP response headers are absent.
// - HTTP response headers do not contain Link headers.
// - Link header does not contain rel="payment-method-manifest".
// - Link header does not contain a valid URL of the same origin.
// - After following the Link header:
// - There's a redirect.
// - HTTP GET returns empty content.
//
// In the case of a web app manifest download, can also also fail when:
// - There's a redirect.
// - HTTP GET on the manifest URL returns empty content.
using PaymentManifestDownloadCallback =
base::OnceCallback<void(const GURL& url,
const std::string& contents,
......@@ -59,9 +64,9 @@ using PaymentManifestDownloadCallback =
// payment method name that is a URL with HTTPS scheme, e.g.,
// https://bobpay.com.
//
// The downloader follows up to three redirects for the HEAD request only (used
// for payment method manifests). Three is enough for known legitimate use cases
// and seems like a good upper bound.
// The downloader follows up to three redirects for the payment method manifest
// request only. Three is enough for known legitimate use cases and seems like a
// good upper bound.
//
// The command line must be initialized to use this class in tests, because it
// checks for --unsafely-treat-insecure-origin-as-secure=<origin> flag. For
......@@ -75,24 +80,20 @@ class PaymentManifestDownloader {
virtual ~PaymentManifestDownloader();
// Download a payment method manifest from |url| via two consecutive HTTP
// requests:
//
// 1) HEAD request for the payment method name |url|. The HTTP response header
// is parsed for Link header that points to the location of the payment
// method manifest file. Example of a relative location:
// Download a payment method manifest from |url| via a GET. The HTTP response
// header is parsed for Link header. If there is no Link header, then the body
// is returned. If there's a Link header, then it is followed exactly once.
// Example header:
//
// Link: <data/payment-manifest.json>; rel="payment-method-manifest"
//
// (This is relative to the payment method URL.) Example of an absolute
// location:
// (This is relative to the payment method URL.) Example of an absolute
// location:
//
// Link: <https://bobpay.com/data/payment-manifest.json>;
// rel="payment-method-manifest"
//
// The absolute location must use HTTPS scheme.
//
// 2) GET request for the payment method manifest file.
// The absolute location must use HTTPS scheme.
//
// |merchant_origin| should be the origin of the iframe that created the
// PaymentRequest object. It is used by security features like
......@@ -129,11 +130,16 @@ class PaymentManifestDownloader {
// Information about an ongoing download request.
struct Download {
enum class Type {
RESPONSE_BODY_OR_LINK_HEADER,
RESPONSE_BODY,
};
Download();
~Download();
int allowed_number_of_redirects = 0;
std::string method;
Type type = Type::RESPONSE_BODY;
url::Origin request_initiator;
GURL original_url;
std::unique_ptr<network::SimpleURLLoader> loader;
......@@ -167,7 +173,7 @@ class PaymentManifestDownloader {
// Overridden in TestDownloader.
virtual void InitiateDownload(const url::Origin& request_initiator,
const GURL& url,
const std::string& method,
Download::Type download_type,
int allowed_number_of_redirects,
PaymentManifestDownloadCallback callback);
......
......@@ -22,12 +22,20 @@ namespace {
using testing::_;
static const char kNoError[] = "";
static constexpr char kNoContent[] = "";
static constexpr char kNoError[] = "";
static constexpr char kNoLinkHeader[] = "";
static constexpr char kNoResponseBody[] = "";
} // namespace
class PaymentMethodManifestDownloaderTest : public testing::Test {
public:
protected:
enum class Headers {
kSend,
kOmit,
};
PaymentMethodManifestDownloaderTest()
: test_url_("https://bobpay.com"),
shared_url_loader_factory_(
......@@ -41,35 +49,34 @@ class PaymentMethodManifestDownloaderTest : public testing::Test {
base::Unretained(this)));
}
~PaymentMethodManifestDownloaderTest() override {}
MOCK_METHOD3(OnManifestDownload,
void(const GURL& unused_url_after_redirects,
const std::string& content,
const std::string& error_message));
void CallComplete(int response_code = 200,
const std::string& link_header = std::string(),
const std::string& response_body = std::string(),
bool send_headers = true) {
void ServerResponse(int response_code,
Headers send_headers,
const std::string& link_header,
const std::string& response_body) {
scoped_refptr<net::HttpResponseHeaders> headers;
if (send_headers) {
if (send_headers == Headers::kSend) {
headers = base::MakeRefCounted<net::HttpResponseHeaders>(std::string());
headers->ReplaceStatusLine(base::StringPrintf(
"HTTP/1.1 %d %s", response_code,
net::GetHttpReasonPhrase(
static_cast<net::HttpStatusCode>(response_code))));
if (!link_header.empty())
headers->AddHeader(link_header);
}
if (!link_header.empty())
headers->AddHeader(link_header);
downloader_.OnURLLoaderCompleteInternal(
downloader_.GetLoaderForTesting(),
downloader_.GetLoaderOriginalURLForTesting(), response_body, headers,
net::OK);
}
void CallRedirect(int redirect_code, const GURL& new_url) {
void ServerRedirect(int redirect_code, const GURL& new_url) {
net::RedirectInfo redirect_info;
redirect_info.status_code = redirect_code;
redirect_info.new_url = new_url;
......@@ -88,127 +95,247 @@ class PaymentMethodManifestDownloaderTest : public testing::Test {
network::TestURLLoaderFactory test_factory_;
scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
PaymentManifestDownloader downloader_;
DISALLOW_COPY_AND_ASSIGN(PaymentMethodManifestDownloaderTest);
};
TEST_F(PaymentMethodManifestDownloaderTest, HttpHeadResponse404IsFailure) {
TEST_F(PaymentMethodManifestDownloaderTest, FirstHttpResponse404IsFailure) {
EXPECT_CALL(
*this,
OnManifestDownload(
_, kNoContent,
"Unable to download payment manifest \"https://bobpay.com/\"."));
ServerResponse(404, Headers::kSend, kNoLinkHeader, kNoResponseBody);
}
TEST_F(PaymentMethodManifestDownloaderTest,
NoHttpHeadersAndEmptyResponseBodyIsFailure) {
EXPECT_CALL(*this,
OnManifestDownload(
_, std::string(),
"Unable to make a HEAD request to \"https://bobpay.com/\" "
"for payment method manifest."));
_, kNoContent,
"No content and no \"Link: rel=payment-method-manifest\" "
"HTTP header found at \"https://bobpay.com/\"."));
CallComplete(404);
ServerResponse(200, Headers::kOmit, kNoLinkHeader, kNoResponseBody);
}
TEST_F(PaymentMethodManifestDownloaderTest, NoHttpHeadersIsFailure) {
EXPECT_CALL(
*this, OnManifestDownload(_, std::string(),
"No \"Link: rel=payment-method-manifest\" HTTP "
"header found at \"https://bobpay.com/\"."));
TEST_F(PaymentMethodManifestDownloaderTest,
NoHttpHeadersButWithResponseBodyIsSuccess) {
EXPECT_CALL(*this, OnManifestDownload(_, "response body", kNoError));
CallComplete(200, std::string(), std::string(), false);
ServerResponse(200, Headers::kOmit, kNoLinkHeader, "response body");
}
TEST_F(PaymentMethodManifestDownloaderTest, EmptyHttpHeaderIsFailure) {
TEST_F(PaymentMethodManifestDownloaderTest,
EmptyHttpHeaderAndEmptyResponseBodyIsFailure) {
EXPECT_CALL(
*this, OnManifestDownload(_, std::string(),
"No \"Link: rel=payment-method-manifest\" HTTP "
"header found at \"https://bobpay.com/\"."));
*this, OnManifestDownload(
_, kNoContent,
"No content and no \"Link: rel=payment-method-manifest\" HTTP "
"header found at \"https://bobpay.com/\"."));
CallComplete(200);
ServerResponse(200, Headers::kSend, kNoLinkHeader, kNoResponseBody);
}
TEST_F(PaymentMethodManifestDownloaderTest, EmptyHttpLinkHeaderIsFailure) {
scoped_refptr<net::HttpResponseHeaders> headers(
new net::HttpResponseHeaders(std::string()));
EXPECT_CALL(
*this, OnManifestDownload(_, std::string(),
"No \"Link: rel=payment-method-manifest\" HTTP "
"header found at \"https://bobpay.com/\"."));
TEST_F(PaymentMethodManifestDownloaderTest,
EmptyHttpHeaderButWithResponseBodyIsSuccess) {
EXPECT_CALL(*this, OnManifestDownload(_, "response content", kNoError));
CallComplete(200, "Link:");
ServerResponse(200, Headers::kSend, kNoLinkHeader, "response content");
}
TEST_F(PaymentMethodManifestDownloaderTest, NoRelInHttpLinkHeaderIsFailure) {
EXPECT_CALL(
*this, OnManifestDownload(_, std::string(),
"No \"Link: rel=payment-method-manifest\" HTTP "
"header found at \"https://bobpay.com/\"."));
TEST_F(PaymentMethodManifestDownloaderTest,
EmptyHttpLinkHeaderWithoutResponseBodyIsFailure) {
EXPECT_CALL(*this,
OnManifestDownload(
_, kNoContent,
"No content and no \"Link: rel=payment-method-manifest\" "
"HTTP header found at \"https://bobpay.com/\"."));
CallComplete(200, "Link: <manifest.json>");
ServerResponse(200, Headers::kSend, "Link:", kNoResponseBody);
}
TEST_F(PaymentMethodManifestDownloaderTest, NoUrlInHttpLinkHeaderIsFailure) {
EXPECT_CALL(
*this, OnManifestDownload(_, std::string(),
"No \"Link: rel=payment-method-manifest\" HTTP "
"header found at \"https://bobpay.com/\"."));
TEST_F(PaymentMethodManifestDownloaderTest,
EmptyHttpLinkHeaderButWithResponseBodyIsSuccess) {
EXPECT_CALL(*this, OnManifestDownload(_, "response body", kNoError));
CallComplete(200, "Link: rel=payment-method-manifest");
ServerResponse(200, Headers::kSend, "Link:", "response body");
}
TEST_F(PaymentMethodManifestDownloaderTest,
NoManifestRellInHttpLinkHeaderIsFailure) {
EXPECT_CALL(
*this, OnManifestDownload(_, std::string(),
"No \"Link: rel=payment-method-manifest\" HTTP "
"header found at \"https://bobpay.com/\"."));
NoRelInHttpLinkHeaderAndNoResponseBodyIsFailure) {
EXPECT_CALL(*this,
OnManifestDownload(
_, std::string(),
"No content and no \"Link: rel=payment-method-manifest\" "
"HTTP header found at \"https://bobpay.com/\"."));
CallComplete(200, "Link: <manifest.json>; rel=web-app-manifest");
ServerResponse(200, Headers::kSend, "Link: <manifest.json>", kNoResponseBody);
}
TEST_F(PaymentMethodManifestDownloaderTest, HttpGetResponse404IsFailure) {
scoped_refptr<net::HttpResponseHeaders> headers(
new net::HttpResponseHeaders(std::string()));
CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
TEST_F(PaymentMethodManifestDownloaderTest,
NoRelInHttpLinkHeaderButWithResponseBodyIsSuccess) {
EXPECT_CALL(*this, OnManifestDownload(_, "response body", kNoError));
ServerResponse(200, Headers::kSend, "Link: <manifest.json>", "response body");
}
TEST_F(PaymentMethodManifestDownloaderTest,
NoUrlInHttpLinkHeaderAndNoResponseBodyIsFailure) {
EXPECT_CALL(*this,
OnManifestDownload(_, std::string(),
OnManifestDownload(
_, kNoContent,
"No content and no \"Link: rel=payment-method-manifest\" "
"HTTP header found at \"https://bobpay.com/\"."));
ServerResponse(200, Headers::kSend, "Link: rel=payment-method-manifest",
kNoResponseBody);
}
TEST_F(PaymentMethodManifestDownloaderTest,
NoUrlInHttpLinkHeaderButWithResponseBodyIsSuccess) {
EXPECT_CALL(*this, OnManifestDownload(_, "response body", kNoError));
ServerResponse(200, Headers::kSend, "Link: rel=payment-method-manifest",
"response body");
}
TEST_F(PaymentMethodManifestDownloaderTest,
NoManifestRellInHttpLinkHeaderAndNoResponseBodyIsFailure) {
EXPECT_CALL(*this,
OnManifestDownload(
_, kNoContent,
"No content and no \"Link: rel=payment-method-manifest\" "
"HTTP header found at \"https://bobpay.com/\"."));
ServerResponse(200, Headers::kSend,
"Link: <manifest.json>; rel=web-app-manifest",
kNoResponseBody);
}
TEST_F(PaymentMethodManifestDownloaderTest,
NoManifestRellInHttpLinkHeaderButWithResponseBodyIsSuccess) {
EXPECT_CALL(*this, OnManifestDownload(_, "response body", kNoError));
ServerResponse(200, Headers::kSend,
"Link: <manifest.json>; rel=web-app-manifest",
"response body");
}
TEST_F(PaymentMethodManifestDownloaderTest, SecondHttpResponse404IsFailure) {
ServerResponse(200, Headers::kSend,
"Link: <manifest.json>; rel=payment-method-manifest",
kNoResponseBody);
EXPECT_CALL(*this,
OnManifestDownload(_, kNoContent,
"Unable to download payment manifest "
"\"https://bobpay.com/manifest.json\"."));
CallComplete(404);
ServerResponse(404, Headers::kSend, kNoLinkHeader, kNoResponseBody);
}
TEST_F(PaymentMethodManifestDownloaderTest, EmptyHttpGetResponseIsFailure) {
CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
TEST_F(PaymentMethodManifestDownloaderTest, EmptySecondResponseIsFailure) {
ServerResponse(200, Headers::kSend,
"Link: <manifest.json>; rel=payment-method-manifest",
kNoResponseBody);
EXPECT_CALL(*this,
OnManifestDownload(_, std::string(),
OnManifestDownload(_, kNoContent,
"No content found in payment manifest "
"\"https://bobpay.com/manifest.json\"."));
ServerResponse(200, Headers::kSend, kNoLinkHeader, kNoResponseBody);
}
TEST_F(PaymentMethodManifestDownloaderTest,
SecondResponseWithoutHeadersIsFailure) {
ServerResponse(200, Headers::kSend,
"Link: <manifest.json>; rel=payment-method-manifest",
kNoResponseBody);
EXPECT_CALL(*this,
OnManifestDownload(_, kNoContent,
"Unable to download payment manifest "
"\"https://bobpay.com/manifest.json\"."));
CallComplete(200, std::string(), std::string(), false);
ServerResponse(200, Headers::kOmit, kNoLinkHeader, kNoResponseBody);
}
TEST_F(PaymentMethodManifestDownloaderTest, NonEmptySecondResponseIsSuccess) {
ServerResponse(200, Headers::kSend,
"Link: <manifest.json>; rel=payment-method-manifest",
kNoResponseBody);
EXPECT_CALL(*this, OnManifestDownload(_, "manifest content", kNoError));
ServerResponse(200, Headers::kSend, kNoLinkHeader, "manifest content");
}
TEST_F(PaymentMethodManifestDownloaderTest, NonEmptyHttpGetResponseIsSuccess) {
CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
TEST_F(PaymentMethodManifestDownloaderTest, FirstResponseCode204IsSuccess) {
ServerResponse(204, Headers::kSend,
"Link: <manifest.json>; rel=payment-method-manifest",
kNoResponseBody);
EXPECT_CALL(*this, OnManifestDownload(_, "manifest content", kNoError));
CallComplete(200, std::string(), "manifest content");
ServerResponse(200, Headers::kSend, kNoLinkHeader, "manifest content");
}
TEST_F(PaymentMethodManifestDownloaderTest, SecondResponseCode204IsFailure) {
ServerResponse(204, Headers::kSend,
"Link: <manifest.json>; rel=payment-method-manifest",
kNoResponseBody);
EXPECT_CALL(*this,
OnManifestDownload(_, kNoContent,
"Unable to download payment manifest "
"\"https://bobpay.com/manifest.json\"."));
ServerResponse(204, Headers::kSend, kNoLinkHeader, "manifest content");
}
TEST_F(PaymentMethodManifestDownloaderTest,
SecondResponseWithLinkHeaderAndNoContentIsFailure) {
ServerResponse(200, Headers::kSend,
"Link: <manifest.json>; rel=payment-method-manifest",
kNoResponseBody);
EXPECT_CALL(*this,
OnManifestDownload(_, kNoContent,
"No content found in payment manifest "
"\"https://bobpay.com/manifest.json\"."));
ServerResponse(200, Headers::kSend,
"Link: <manifest.json>; rel=payment-method-manifest",
kNoResponseBody);
}
TEST_F(PaymentMethodManifestDownloaderTest, HeaderResponseCode204IsSuccess) {
CallComplete(204, "Link: <manifest.json>; rel=payment-method-manifest");
TEST_F(PaymentMethodManifestDownloaderTest,
SecondResponseWithLinkHeaderAndWithContentReturnsTheContent) {
ServerResponse(200, Headers::kSend,
"Link: <manifest.json>; rel=payment-method-manifest",
kNoResponseBody);
EXPECT_CALL(*this, OnManifestDownload(_, "manifest content", kNoError));
CallComplete(200, std::string(), "manifest content");
ServerResponse(200, Headers::kSend,
"Link: <manifest.json>; rel=payment-method-manifest",
"manifest content");
}
TEST_F(PaymentMethodManifestDownloaderTest, RelativeHttpHeaderLinkUrl) {
CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
ServerResponse(200, Headers::kSend,
"Link: <manifest.json>; rel=payment-method-manifest",
kNoResponseBody);
EXPECT_EQ("https://bobpay.com/manifest.json", GetOriginalURL());
}
TEST_F(PaymentMethodManifestDownloaderTest, AbsoluteHttpsHeaderLinkUrl) {
CallComplete(200,
"Link: <https://bobpay.com/manifest.json>; "
"rel=payment-method-manifest");
ServerResponse(200, Headers::kSend,
"Link: <https://bobpay.com/manifest.json>; "
"rel=payment-method-manifest",
kNoResponseBody);
EXPECT_EQ("https://bobpay.com/manifest.json", GetOriginalURL());
}
......@@ -217,131 +344,152 @@ TEST_F(PaymentMethodManifestDownloaderTest, AbsoluteHttpHeaderLinkUrl) {
EXPECT_CALL(
*this,
OnManifestDownload(
_, std::string(),
_, kNoContent,
"\"http://bobpay.com/manifest.json\" is not a valid payment manifest "
"URL with HTTPS scheme (or HTTP scheme for localhost)."));
CallComplete(200,
"Link: <http://bobpay.com/manifest.json>; "
"rel=payment-method-manifest");
ServerResponse(
200, Headers::kSend,
"Link: <http://bobpay.com/manifest.json>; rel=payment-method-manifest",
kNoResponseBody);
}
TEST_F(PaymentMethodManifestDownloaderTest, 300IsUnsupportedRedirect) {
EXPECT_CALL(*this,
OnManifestDownload(
_, std::string(),
_, kNoContent,
"HTTP status code 300 \"Multiple Choices\" not allowed for "
"payment method manifest \"https://bobpay.com/\"."));
CallRedirect(300, GURL("https://pay.bobpay.com"));
ServerRedirect(300, GURL("https://pay.bobpay.com"));
}
TEST_F(PaymentMethodManifestDownloaderTest, 301And302AreSupportedRedirects) {
CallRedirect(301, GURL("https://pay.bobpay.com"));
ServerRedirect(301, GURL("https://pay.bobpay.com"));
EXPECT_EQ(GetOriginalURL(), GURL("https://pay.bobpay.com"));
CallRedirect(302, GURL("https://newpay.bobpay.com"));
ServerRedirect(302, GURL("https://newpay.bobpay.com"));
EXPECT_EQ(GetOriginalURL(), GURL("https://newpay.bobpay.com"));
CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
ServerResponse(200, Headers::kSend,
"Link: <manifest.json>; rel=payment-method-manifest",
kNoResponseBody);
EXPECT_CALL(*this, OnManifestDownload(_, "manifest content", kNoError));
CallComplete(200, std::string(), "manifest content");
ServerResponse(200, Headers::kSend, kNoLinkHeader, "manifest content");
}
TEST_F(PaymentMethodManifestDownloaderTest,
CannotRedirectAfterFollowingLinkHeader) {
ServerResponse(200, Headers::kSend,
"Link: <manifest.json>; rel=payment-method-manifest",
kNoResponseBody);
EXPECT_CALL(*this, OnManifestDownload(
_, kNoContent,
"Unable to download the payment manifest because "
"reached the maximum number of redirects."));
ServerRedirect(301, GURL("https://pay.bobpay.com"));
}
TEST_F(PaymentMethodManifestDownloaderTest, 302And303AreSupportedRedirects) {
CallRedirect(302, GURL("https://pay.bobpay.com"));
ServerRedirect(302, GURL("https://pay.bobpay.com"));
EXPECT_EQ(GetOriginalURL(), GURL("https://pay.bobpay.com"));
CallRedirect(303, GURL("https://newpay.bobpay.com"));
ServerRedirect(303, GURL("https://newpay.bobpay.com"));
EXPECT_EQ(GetOriginalURL(), GURL("https://newpay.bobpay.com"));
CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
ServerResponse(200, Headers::kSend,
"Link: <manifest.json>; rel=payment-method-manifest",
kNoResponseBody);
EXPECT_CALL(*this, OnManifestDownload(_, "manifest content", kNoError));
CallComplete(200, std::string(), "manifest content");
ServerResponse(200, Headers::kSend, kNoLinkHeader, "manifest content");
}
TEST_F(PaymentMethodManifestDownloaderTest, 304IsUnsupportedRedirect) {
EXPECT_CALL(*this,
OnManifestDownload(
_, std::string(),
_, kNoContent,
"HTTP status code 304 \"Not Modified\" not allowed for "
"payment method manifest \"https://bobpay.com/\"."));
CallRedirect(304, GURL("https://pay.bobpay.com"));
ServerRedirect(304, GURL("https://pay.bobpay.com"));
}
TEST_F(PaymentMethodManifestDownloaderTest, 305IsUnsupportedRedirect) {
EXPECT_CALL(*this, OnManifestDownload(
_, std::string(),
_, kNoContent,
"HTTP status code 305 \"Use Proxy\" not allowed for "
"payment method manifest \"https://bobpay.com/\"."));
CallRedirect(305, GURL("https://pay.bobpay.com"));
ServerRedirect(305, GURL("https://pay.bobpay.com"));
}
TEST_F(PaymentMethodManifestDownloaderTest, 307And308AreSupportedRedirects) {
CallRedirect(307, GURL("https://pay.bobpay.com"));
ServerRedirect(307, GURL("https://pay.bobpay.com"));
EXPECT_EQ(GetOriginalURL(), GURL("https://pay.bobpay.com"));
CallRedirect(308, GURL("https://newpay.bobpay.com"));
ServerRedirect(308, GURL("https://newpay.bobpay.com"));
EXPECT_EQ(GetOriginalURL(), GURL("https://newpay.bobpay.com"));
CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
ServerResponse(200, Headers::kSend,
"Link: <manifest.json>; rel=payment-method-manifest",
kNoResponseBody);
EXPECT_CALL(*this, OnManifestDownload(_, "manifest content", kNoError));
CallComplete(200, std::string(), "manifest content");
ServerResponse(200, Headers::kSend, kNoLinkHeader, "manifest content");
}
TEST_F(PaymentMethodManifestDownloaderTest, NoMoreThanThreeRedirects) {
CallRedirect(301, GURL("https://pay.bobpay.com"));
ServerRedirect(301, GURL("https://pay.bobpay.com"));
EXPECT_EQ(GetOriginalURL(), GURL("https://pay.bobpay.com"));
CallRedirect(302, GURL("https://oldpay.bobpay.com"));
ServerRedirect(302, GURL("https://oldpay.bobpay.com"));
EXPECT_EQ(GetOriginalURL(), GURL("https://oldpay.bobpay.com"));
CallRedirect(308, GURL("https://newpay.bobpay.com"));
ServerRedirect(308, GURL("https://newpay.bobpay.com"));
EXPECT_EQ(GetOriginalURL(), GURL("https://newpay.bobpay.com"));
EXPECT_CALL(*this, OnManifestDownload(
_, std::string(),
_, kNoContent,
"Unable to download the payment manifest because "
"reached the maximum number of redirects."));
CallRedirect(308, GURL("https://newpay.bobpay.com"));
ServerRedirect(308, GURL("https://newpay.bobpay.com"));
}
TEST_F(PaymentMethodManifestDownloaderTest, InvalidRedirectUrlIsFailure) {
EXPECT_CALL(*this, OnManifestDownload(
_, std::string(),
_, kNoContent,
"\"\" is not a valid payment manifest URL with HTTPS "
"scheme (or HTTP scheme for localhost)."));
CallRedirect(308, GURL("pay.bobpay.com"));
ServerRedirect(308, GURL("pay.bobpay.com"));
}
TEST_F(PaymentMethodManifestDownloaderTest, NotAllowCrossSiteRedirects) {
EXPECT_CALL(
*this,
OnManifestDownload(
_, std::string(),
_, kNoContent,
"Cross-site redirect from \"https://bobpay.com/\" to "
"\"https://alicepay.com/\" not allowed for payment manifests."));
CallRedirect(301, GURL("https://alicepay.com"));
ServerRedirect(301, GURL("https://alicepay.com"));
}
class WebAppManifestDownloaderTest : public testing::Test {
......@@ -366,8 +514,7 @@ class WebAppManifestDownloaderTest : public testing::Test {
const std::string& content,
const std::string& error_message));
void CallComplete(int response_code,
const std::string& response_body = std::string()) {
void ServerResponse(int response_code, const std::string& response_body) {
scoped_refptr<net::HttpResponseHeaders> headers =
base::MakeRefCounted<net::HttpResponseHeaders>(std::string());
headers->ReplaceStatusLine(base::StringPrintf(
......@@ -393,22 +540,26 @@ TEST_F(WebAppManifestDownloaderTest, HttpGetResponse404IsFailure) {
EXPECT_CALL(
*this,
OnManifestDownload(
_, std::string(),
_, kNoContent,
"Unable to download payment manifest \"https://bobpay.com/\"."));
CallComplete(404);
ServerResponse(404, kNoResponseBody);
}
TEST_F(WebAppManifestDownloaderTest, EmptyHttpGetResponseIsFailure) {
EXPECT_CALL(*this, OnManifestDownload(_, std::string(), kNoError));
EXPECT_CALL(
*this,
OnManifestDownload(
_, kNoContent,
"No content found in payment manifest \"https://bobpay.com/\"."));
CallComplete(200);
ServerResponse(200, kNoResponseBody);
}
TEST_F(WebAppManifestDownloaderTest, NonEmptyHttpGetResponseIsSuccess) {
EXPECT_CALL(*this, OnManifestDownload(_, "manifest content", kNoError));
CallComplete(200, "manifest content");
ServerResponse(200, "manifest content");
}
} // namespace payments
......@@ -29,11 +29,11 @@ void TestDownloader::AddTestServerURL(const std::string& prefix,
void TestDownloader::InitiateDownload(
const url::Origin& request_initiator,
const GURL& url,
const std::string& method,
Download::Type download_type,
int allowed_number_of_redirects,
PaymentManifestDownloadCallback callback) {
PaymentManifestDownloader::InitiateDownload(
request_initiator, FindTestServerURL(url), method,
request_initiator, FindTestServerURL(url), download_type,
allowed_number_of_redirects, std::move(callback));
}
......
......@@ -97,7 +97,7 @@ class TestDownloader : public PaymentManifestDownloader {
// PaymentManifestDownloader implementation.
void InitiateDownload(const url::Origin& request_initiator,
const GURL& url,
const std::string& method,
Download::Type download_type,
int allowed_number_of_redirects,
PaymentManifestDownloadCallback callback) override;
......
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