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
......
......@@ -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);
......
......@@ -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