blob: 5e039eae75a726a8e922e39b4eb39b8320f21133 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_PAYMENTS_CORE_PAYMENT_MANIFEST_DOWNLOADER_H_
#define COMPONENTS_PAYMENTS_CORE_PAYMENT_MANIFEST_DOWNLOADER_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/functional/callback.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "services/network/public/mojom/url_response_head.mojom-forward.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace net {
class HttpResponseHeaders;
struct RedirectInfo;
} // namespace net
namespace network {
class SharedURLLoaderFactory;
class SimpleURLLoader;
} // namespace network
namespace payments {
class CSPChecker;
class ErrorLogger;
// Called on completed download of a manifest |contents| from |url|, which is
// the final URL after following the redirects, if any.
//
// 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 payment method
// manifest.)
//
// In the case of a payment method manifest download, can also fail when:
// - More than three redirects.
// - Cross-site redirects.
// - 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,
const std::string& error_message)>;
// Downloader of the payment method manifest and web-app manifest based on the
// payment method name that is a URL with HTTPS scheme, e.g.,
// https://bobpay.com.
//
// 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
// example:
// base::CommandLine::Init(0, nullptr);
class PaymentManifestDownloader {
public:
PaymentManifestDownloader(
std::unique_ptr<ErrorLogger> log,
base::WeakPtr<CSPChecker> csp_checker,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
PaymentManifestDownloader(const PaymentManifestDownloader&) = delete;
PaymentManifestDownloader& operator=(const PaymentManifestDownloader&) =
delete;
virtual ~PaymentManifestDownloader();
// 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:
//
// Link: <https://bobpay.com/data/payment-manifest.json>;
// rel="payment-method-manifest"
//
// 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
// 'Sec-Fetch-Site' and 'Cross-Origin-Resource-Policy'.
//
// |url| should be valid according to UrlUtil::IsValidManifestUrl() to
// download.
void DownloadPaymentMethodManifest(const url::Origin& merchant_origin,
const GURL& url,
PaymentManifestDownloadCallback callback);
// Download a web app manifest from |url| via a single HTTP request:
//
// 1) GET request for the payment method name.
//
// |payment_method_manifest_origin| should be the origin of the payment method
// manifest that is pointing to this web app manifest. It is used for security
// features like 'Sec-Fetch-Site' and 'Cross-Origin-Resource-Policy'.
//
// |url| should be valid according to UrlUtil::IsValidManifestUrl() to
// download.
void DownloadWebAppManifest(const url::Origin& payment_method_manifest_origin,
const GURL& url,
PaymentManifestDownloadCallback callback);
// Overridden in TestDownloader to convert |url| to a test server URL. The
// default implementation here simply returns |url|.
virtual GURL FindTestServerURL(const GURL& url) const;
// Overridden in TestDownloader to allow modifying CSP. Should not be called
// in production.
virtual void SetCSPCheckerForTesting(base::WeakPtr<CSPChecker> csp_checker);
private:
friend class PaymentManifestDownloaderTestBase;
friend class TestDownloader;
// Information about an ongoing download request.
struct Download {
enum class Type {
LINK_HEADER,
RESPONSE_BODY,
};
Download();
~Download();
// Returns true if this download is an HTTP HEAD request for a payment
// manifest.
bool IsLinkHeaderDownload() const;
// Returns true if this download is an HTTP GET request either for payment
// method manifest or for a web app manifest file.
bool IsResponseBodyDownload() const;
int allowed_number_of_redirects = 0;
Type type = Type::RESPONSE_BODY;
url::Origin request_initiator;
GURL original_url;
GURL url_before_redirects;
bool did_follow_redirect = false;
std::unique_ptr<network::SimpleURLLoader> loader;
PaymentManifestDownloadCallback callback;
};
// Called by SimpleURLLoader on a redirect.
void OnURLLoaderRedirect(network::SimpleURLLoader* url_loader,
const GURL& url_before_redirect,
const net::RedirectInfo& redirect_info,
const network::mojom::URLResponseHead& response_head,
std::vector<std::string>* to_be_removed_headers);
// Called by SimpleURLLoader on completion.
void OnURLLoaderComplete(network::SimpleURLLoader* url_loader,
std::optional<std::string> response_body);
// Internally called by OnURLLoaderComplete, exposed to ease unit tests.
void OnURLLoaderCompleteInternal(
network::SimpleURLLoader* url_loader,
const GURL& final_url,
const std::string& response_body,
scoped_refptr<net::HttpResponseHeaders> headers,
int net_error);
// Called by unittests to get the one in-progress loader.
network::SimpleURLLoader* GetLoaderForTesting();
// Called by unittests to get the original URL of the in-progress loader.
GURL GetLoaderOriginalURLForTesting();
// Overridden in TestDownloader.
virtual void InitiateDownload(const url::Origin& request_initiator,
const GURL& url,
const GURL& url_before_redirects,
bool did_follow_redirect,
Download::Type download_type,
int allowed_number_of_redirects,
PaymentManifestDownloadCallback callback);
void OnCSPCheck(std::unique_ptr<Download> download, bool csp_allowed);
std::unique_ptr<ErrorLogger> log_;
base::WeakPtr<CSPChecker> csp_checker_;
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
// Downloads are identified by network::SimpleURLLoader pointers, because
// that's the only unique piece of information that OnURLLoaderComplete()
// receives. Can't rely on the URL of the download, because of possible
// collision between HEAD and GET requests.
std::map<const network::SimpleURLLoader*, std::unique_ptr<Download>>
downloads_;
base::WeakPtrFactory<PaymentManifestDownloader> weak_ptr_factory_{this};
};
} // namespace payments
#endif // COMPONENTS_PAYMENTS_CORE_PAYMENT_MANIFEST_DOWNLOADER_H_