1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
|
// 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_WITH_FALLBACK_TO_RESPONSE_BODY,
FALLBACK_TO_RESPONSE_BODY,
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_
|