diff --git a/components/payments/core/features.cc b/components/payments/core/features.cc index ebc1b744dcaccb45897df7dad8aefac7d2b633dc..37ac1341c2a49c54cc2d3903612c2c1a04a49cfc 100644 --- a/components/payments/core/features.cc +++ b/components/payments/core/features.cc @@ -68,5 +68,9 @@ BASE_FEATURE(kPaymentHandlerMinimalHeaderUX, "PaymentHandlerMinimalHeaderUX", base::FEATURE_ENABLED_BY_DEFAULT); +BASE_FEATURE(kPaymentHandlerRequireLinkHeader, + "PaymentHandlerRequireLinkHeader", + base::FEATURE_ENABLED_BY_DEFAULT); + } // namespace features } // namespace payments diff --git a/components/payments/core/features.h b/components/payments/core/features.h index 860db566656fc508fa428e8dec7be3b87e150735..06a62cb674afea945e2a21dc139391ac6b71206c 100644 --- a/components/payments/core/features.h +++ b/components/payments/core/features.h @@ -51,6 +51,10 @@ BASE_DECLARE_FEATURE(kSecurePaymentConfirmationUseCredentialStoreAPIs); // See https://crbug.com/1385136. BASE_DECLARE_FEATURE(kPaymentHandlerMinimalHeaderUX); +// If enabled, the payment method manifest fetch for Payment Handler must go via +// a Link header with rel="payment-method-manifest". +BASE_DECLARE_FEATURE(kPaymentHandlerRequireLinkHeader); + } // namespace features } // namespace payments diff --git a/components/payments/core/native_error_strings.cc b/components/payments/core/native_error_strings.cc index fde06417370adb2aee3fbc77bc9e4e9ca2e8b3f2..b017c9ac7e500f4ccc74fadbcc06dd48f16a53ac 100644 --- a/components/payments/core/native_error_strings.cc +++ b/components/payments/core/native_error_strings.cc @@ -97,6 +97,9 @@ const char kPaymentManifestCrossSiteRedirectNotAllowed[] = const char kPaymentManifestDownloadFailed[] = "Unable to download payment manifest \"$1\"."; +const char kPaymentManifestDownloadFailedWithHttpStatusCode[] = + "Unable to download payment manifest \"$1\". HTTP $2 $3."; + const char kPaymentManifestCSPDenied[] = "Content Security Policy denied the download of payment manifest \"$1\"."; @@ -186,6 +189,9 @@ const char kCanMakePaymentEventNoExplicitlyVerifiedMethods[] = const char kGenericPaymentMethodNotSupportedMessage[] = "Payment method not supported."; +const char kNoLinkHeader[] = + "No \"Link: rel=payment-method-manifest\" HTTP header found at \"$1\"."; + const char kNoContentAndNoLinkHeader[] = "No content and no \"Link: rel=payment-method-manifest\" HTTP header found " "at \"$1\"."; diff --git a/components/payments/core/native_error_strings.h b/components/payments/core/native_error_strings.h index 2f164bf3d76b2207412ea518cc1d3ebb9ac161c5..b2f12ccf372958fbde6f63c9f75487f11522b9ec 100644 --- a/components/payments/core/native_error_strings.h +++ b/components/payments/core/native_error_strings.h @@ -125,6 +125,12 @@ extern const char kPaymentManifestCrossSiteRedirectNotAllowed[]; // be used with base::ReplaceStringPlaceholders(fmt, {A}, nullptr). extern const char kPaymentManifestDownloadFailed[]; +// Used when downloading payment manifest URL A has failed because of HTTP +// status code B. This format should be used with +// base::ReplaceStringPlaceholders( +// fmt, {A, base::NumberToString(B), net::GetHttpReasonPhrase(B)}, nullptr). +extern const char kPaymentManifestDownloadFailedWithHttpStatusCode[]; + // Used when Content Security Policy (CSP) denied downloading payment manifest // URL A. This format should be used with base::ReplaceStringPlaceholders(fmt, // {A}, nullptr). @@ -216,6 +222,11 @@ 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 kNoLinkHeader[]; + +// Used for errors downloading the payment method manifest. This format should +// be used with base::ReplaceStringPlaceholders(fmt, {A}, nullptr). + extern const char kNoContentAndNoLinkHeader[]; // Used when the downloaded payment manifest A is empty. This format should be diff --git a/components/payments/core/payment_manifest_downloader.cc b/components/payments/core/payment_manifest_downloader.cc index e8c5036444e7f4d98ef5c19916f14dbc4ed45d1c..d994de2db3cb469fafb25f4802e5f3b2060c1cf7 100644 --- a/components/payments/core/payment_manifest_downloader.cc +++ b/components/payments/core/payment_manifest_downloader.cc @@ -9,6 +9,7 @@ #include "base/check_op.h" #include "base/containers/contains.h" +#include "base/feature_list.h" #include "base/functional/bind.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" @@ -17,12 +18,14 @@ #include "components/link_header_util/link_header_util.h" #include "components/payments/core/csp_checker.h" #include "components/payments/core/error_logger.h" +#include "components/payments/core/features.h" #include "components/payments/core/native_error_strings.h" #include "components/payments/core/url_util.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "net/base/url_util.h" +#include "net/http/http_request_headers.h" #include "net/http/http_response_headers.h" #include "net/http/http_status_code.h" #include "net/http/http_util.h" @@ -42,6 +45,19 @@ static_assert(kMaxManifestSize <= network::SimpleURLLoader::kMaxBoundedStringDownloadSize, "Max manifest size bigger than largest allowed download size"); +void RespondWithHttpStatusCodeError(const GURL& final_url, + net::HttpStatusCode http_status_code, + const ErrorLogger& log, + PaymentManifestDownloadCallback callback) { + std::string error_message = base::ReplaceStringPlaceholders( + errors::kPaymentManifestDownloadFailedWithHttpStatusCode, + {final_url.spec(), base::NumberToString(http_status_code), + net::GetHttpReasonPhrase(http_status_code)}, + nullptr); + log.Error(error_message); + std::move(callback).Run(final_url, std::string(), error_message); +} + // Invokes |callback| with |error_format|. void RespondWithError(const base::StringPiece& error_format, const GURL& final_url, @@ -128,7 +144,7 @@ void PaymentManifestDownloader::DownloadPaymentMethodManifest( // Restrict number of redirects for efficiency and breaking circle. InitiateDownload(merchant_origin, url, /*url_before_redirects=*/url, /*did_follow_redirect=*/false, - Download::Type::RESPONSE_BODY_OR_LINK_HEADER, + Download::Type::LINK_HEADER_WITH_FALLBACK_TO_RESPONSE_BODY, /*allowed_number_of_redirects=*/3, std::move(callback)); } @@ -156,6 +172,14 @@ PaymentManifestDownloader::Download::Download() = default; PaymentManifestDownloader::Download::~Download() = default; +bool PaymentManifestDownloader::Download::IsLinkHeaderDownload() const { + return type == Type::LINK_HEADER_WITH_FALLBACK_TO_RESPONSE_BODY; +} + +bool PaymentManifestDownloader::Download::IsResponseBodyDownload() const { + return type == Type::FALLBACK_TO_RESPONSE_BODY || type == Type::RESPONSE_BODY; +} + void PaymentManifestDownloader::OnURLLoaderRedirect( network::SimpleURLLoader* url_loader, const GURL& url_before_redirect, @@ -171,7 +195,7 @@ void PaymentManifestDownloader::OnURLLoaderRedirect( // Manually follow some type of redirects. std::string error_message; if (download->allowed_number_of_redirects > 0) { - DCHECK_EQ(Download::Type::RESPONSE_BODY_OR_LINK_HEADER, download->type); + DCHECK(download->IsLinkHeaderDownload()); GURL redirect_url = ParseRedirectUrl(redirect_info, download->original_url, *log_, &error_message); if (!redirect_url.is_empty()) { @@ -184,7 +208,7 @@ void PaymentManifestDownloader::OnURLLoaderRedirect( download->request_initiator, redirect_url, /*url_before_redirects=*/download->url_before_redirects, /*did_follow_redirect=*/true, - Download::Type::RESPONSE_BODY_OR_LINK_HEADER, + Download::Type::LINK_HEADER_WITH_FALLBACK_TO_RESPONSE_BODY, --download->allowed_number_of_redirects, std::move(download->callback)); return; @@ -236,22 +260,27 @@ void PaymentManifestDownloader::OnURLLoaderCompleteInternal( } std::string 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)); + if (download->IsResponseBodyDownload()) { + if (headers && headers->response_code() != net::HTTP_OK) { + RespondWithHttpStatusCodeError( + final_url, static_cast(headers->response_code()), + *log_, std::move(download->callback)); } else { - RespondWithContent(response_body, errors::kNoContentInPaymentManifest, - final_url, *log_, std::move(download->callback)); + RespondWithContent( + response_body, + download->type == Download::Type::FALLBACK_TO_RESPONSE_BODY + ? errors::kNoContentAndNoLinkHeader + : errors::kNoContentInPaymentManifest, + final_url, *log_, std::move(download->callback)); } return; } - DCHECK_EQ(Download::Type::RESPONSE_BODY_OR_LINK_HEADER, download->type); + DCHECK(download->IsLinkHeaderDownload()); if (!headers) { - RespondWithContent(response_body, errors::kNoContentAndNoLinkHeader, - final_url, *log_, std::move(download->callback)); + // HTTP HEAD response has no headers; possibly fallback to HTTP GET. + TryFallbackToDownloadingResponseBody(final_url, std::move(download)); return; } @@ -265,8 +294,8 @@ void PaymentManifestDownloader::OnURLLoaderCompleteInternal( 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)); + // HTTP HEAD response has no Link header; possibly fallback to HTTP GET. + TryFallbackToDownloadingResponseBody(final_url, std::move(download)); return; } @@ -317,8 +346,33 @@ void PaymentManifestDownloader::OnURLLoaderCompleteInternal( } } - RespondWithContent(response_body, errors::kNoContentAndNoLinkHeader, - final_url, *log_, std::move(download->callback)); + // HTTP HEAD response has no Link header that has a + // rel="payment-method-manifest" entry; possibly fallback to HTTP GET. + TryFallbackToDownloadingResponseBody(final_url, std::move(download)); +} + +void PaymentManifestDownloader::TryFallbackToDownloadingResponseBody( + const GURL& url_to_download, + std::unique_ptr download_info) { + if (base::FeatureList::IsEnabled( + features::kPaymentHandlerRequireLinkHeader)) { + // Not allowed to fallback, because the payment method manifest load must + // have a Link header. + std::string error_message = base::ReplaceStringPlaceholders( + errors::kNoLinkHeader, {url_to_download.spec()}, nullptr); + log_->Error(error_message); + std::move(download_info->callback) + .Run(url_to_download, std::string(), error_message); + } else { + InitiateDownload( + /*request_initiator=*/download_info->request_initiator, + /*url=*/url_to_download, + /*url_before_redirects=*/download_info->url_before_redirects, + /*did_follow_redirect=*/download_info->did_follow_redirect, + /*download_type=*/Download::Type::FALLBACK_TO_RESPONSE_BODY, + /*allowed_number_of_redirects=*/0, + /*callback=*/std::move(download_info->callback)); + } } network::SimpleURLLoader* PaymentManifestDownloader::GetLoaderForTesting() { @@ -344,7 +398,8 @@ void PaymentManifestDownloader::InitiateDownload( // 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); + download_type == + Download::Type::LINK_HEADER_WITH_FALLBACK_TO_RESPONSE_BODY); net::NetworkTrafficAnnotationTag traffic_annotation = net::DefineNetworkTrafficAnnotation("payment_manifest_downloader", R"( @@ -369,7 +424,17 @@ void PaymentManifestDownloader::InitiateDownload( auto resource_request = std::make_unique(); resource_request->request_initiator = request_initiator; resource_request->url = url; - resource_request->method = "GET"; + + switch (download_type) { + case Download::Type::LINK_HEADER_WITH_FALLBACK_TO_RESPONSE_BODY: + resource_request->method = net::HttpRequestHeaders::kHeadMethod; + break; + case Download::Type::FALLBACK_TO_RESPONSE_BODY: + // Intentional fall through. + case Download::Type::RESPONSE_BODY: + resource_request->method = net::HttpRequestHeaders::kGetMethod; + break; + } resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; std::unique_ptr loader = network::SimpleURLLoader::Create(std::move(resource_request), diff --git a/components/payments/core/payment_manifest_downloader.h b/components/payments/core/payment_manifest_downloader.h index f85745019f34fd912e413d5c4ea863dbb5895818..ded08e0cea34b13e170e9565ed6dcfd7c7b7aca2 100644 --- a/components/payments/core/payment_manifest_downloader.h +++ b/components/payments/core/payment_manifest_downloader.h @@ -139,12 +139,21 @@ class PaymentManifestDownloader { // Information about an ongoing download request. struct Download { enum class Type { - RESPONSE_BODY_OR_LINK_HEADER, + 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; @@ -175,6 +184,10 @@ class PaymentManifestDownloader { scoped_refptr headers, int net_error); + void TryFallbackToDownloadingResponseBody( + const GURL& url_to_download, + std::unique_ptr download_info); + // Called by unittests to get the one in-progress loader. network::SimpleURLLoader* GetLoaderForTesting(); diff --git a/components/payments/core/payment_manifest_downloader_unittest.cc b/components/payments/core/payment_manifest_downloader_unittest.cc index 8cab806661ac15f334e6adeac49ad9db02decde4..2271e403a20d43f42c9c3ef6015b8925ca9eae34 100644 --- a/components/payments/core/payment_manifest_downloader_unittest.cc +++ b/components/payments/core/payment_manifest_downloader_unittest.cc @@ -9,9 +9,11 @@ #include "base/functional/bind.h" #include "base/strings/stringprintf.h" +#include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "components/payments/core/const_csp_checker.h" #include "components/payments/core/error_logger.h" +#include "components/payments/core/features.h" #include "net/base/net_errors.h" #include "net/http/http_response_headers.h" #include "services/network/public/cpp/simple_url_loader.h" @@ -143,33 +145,41 @@ TEST_F(PaymentMethodManifestDownloaderTest, EXPECT_CALL(*this, OnManifestDownload( _, kNoContent, - "No content and no \"Link: rel=payment-method-manifest\" " - "HTTP header found at \"https://bobpay.test/\".")); + "No \"Link: rel=payment-method-manifest\" HTTP header found " + "at \"https://bobpay.test/\".")); ServerResponse(200, Headers::kOmit, kNoLinkHeader, kNoResponseBody, net::OK); } TEST_F(PaymentMethodManifestDownloaderTest, - NoHttpHeadersButWithResponseBodyIsSuccess) { - EXPECT_CALL(*this, OnManifestDownload(_, "response body", kNoError)); + NoHttpHeadersButWithResponseBodyIsFailure) { + EXPECT_CALL(*this, + OnManifestDownload( + _, kNoContent, + "No \"Link: rel=payment-method-manifest\" HTTP header found " + "at \"https://bobpay.test/\".")); ServerResponse(200, Headers::kOmit, kNoLinkHeader, "response body", net::OK); } TEST_F(PaymentMethodManifestDownloaderTest, EmptyHttpHeaderAndEmptyResponseBodyIsFailure) { - EXPECT_CALL( - *this, OnManifestDownload( - _, kNoContent, - "No content and no \"Link: rel=payment-method-manifest\" HTTP " - "header found at \"https://bobpay.test/\".")); + EXPECT_CALL(*this, + OnManifestDownload( + _, kNoContent, + "No \"Link: rel=payment-method-manifest\" HTTP header found " + "at \"https://bobpay.test/\".")); ServerResponse(200, Headers::kSend, kNoLinkHeader, kNoResponseBody, net::OK); } TEST_F(PaymentMethodManifestDownloaderTest, - EmptyHttpHeaderButWithResponseBodyIsSuccess) { - EXPECT_CALL(*this, OnManifestDownload(_, "response content", kNoError)); + EmptyHttpHeaderButWithResponseBodyIsFailure) { + EXPECT_CALL(*this, + OnManifestDownload( + _, kNoContent, + "No \"Link: rel=payment-method-manifest\" HTTP header found " + "at \"https://bobpay.test/\".")); ServerResponse(200, Headers::kSend, kNoLinkHeader, "response content", net::OK); @@ -180,16 +190,20 @@ TEST_F(PaymentMethodManifestDownloaderTest, EXPECT_CALL(*this, OnManifestDownload( _, kNoContent, - "No content and no \"Link: rel=payment-method-manifest\" " - "HTTP header found at \"https://bobpay.test/\".")); + "No \"Link: rel=payment-method-manifest\" HTTP header found " + "at \"https://bobpay.test/\".")); ServerResponse(200, Headers::kSend, kEmptyLinkHeader, kNoResponseBody, net::OK); } TEST_F(PaymentMethodManifestDownloaderTest, - EmptyHttpLinkHeaderButWithResponseBodyIsSuccess) { - EXPECT_CALL(*this, OnManifestDownload(_, "response body", kNoError)); + EmptyHttpLinkHeaderButWithResponseBodyIsFailure) { + EXPECT_CALL(*this, + OnManifestDownload( + _, kNoContent, + "No \"Link: rel=payment-method-manifest\" HTTP header found " + "at \"https://bobpay.test/\".")); ServerResponse(200, Headers::kSend, kEmptyLinkHeader, "response body", net::OK); @@ -199,17 +213,21 @@ TEST_F(PaymentMethodManifestDownloaderTest, NoRelInHttpLinkHeaderAndNoResponseBodyIsFailure) { EXPECT_CALL(*this, OnManifestDownload( - _, std::string(), - "No content and no \"Link: rel=payment-method-manifest\" " - "HTTP header found at \"https://bobpay.test/\".")); + _, kNoContent, + "No \"Link: rel=payment-method-manifest\" HTTP header found " + "at \"https://bobpay.test/\".")); ServerResponse(200, Headers::kSend, "", kNoResponseBody, net::OK); } TEST_F(PaymentMethodManifestDownloaderTest, - NoRelInHttpLinkHeaderButWithResponseBodyIsSuccess) { - EXPECT_CALL(*this, OnManifestDownload(_, "response body", kNoError)); + NoRelInHttpLinkHeaderButWithResponseBodyIsFailure) { + EXPECT_CALL(*this, + OnManifestDownload( + _, kNoContent, + "No \"Link: rel=payment-method-manifest\" HTTP header found " + "at \"https://bobpay.test/\".")); ServerResponse(200, Headers::kSend, "", "response body", net::OK); @@ -220,16 +238,20 @@ TEST_F(PaymentMethodManifestDownloaderTest, EXPECT_CALL(*this, OnManifestDownload( _, kNoContent, - "No content and no \"Link: rel=payment-method-manifest\" " - "HTTP header found at \"https://bobpay.test/\".")); + "No \"Link: rel=payment-method-manifest\" HTTP header found " + "at \"https://bobpay.test/\".")); ServerResponse(200, Headers::kSend, "rel=payment-method-manifest", kNoResponseBody, net::OK); } TEST_F(PaymentMethodManifestDownloaderTest, - NoUrlInHttpLinkHeaderButWithResponseBodyIsSuccess) { - EXPECT_CALL(*this, OnManifestDownload(_, "response body", kNoError)); + NoUrlInHttpLinkHeaderButWithResponseBodyIsFailure) { + EXPECT_CALL(*this, + OnManifestDownload( + _, kNoContent, + "No \"Link: rel=payment-method-manifest\" HTTP header found " + "at \"https://bobpay.test/\".")); ServerResponse(200, Headers::kSend, "rel=payment-method-manifest", "response body", net::OK); @@ -240,16 +262,20 @@ TEST_F(PaymentMethodManifestDownloaderTest, EXPECT_CALL(*this, OnManifestDownload( _, kNoContent, - "No content and no \"Link: rel=payment-method-manifest\" " - "HTTP header found at \"https://bobpay.test/\".")); + "No \"Link: rel=payment-method-manifest\" HTTP header found " + "at \"https://bobpay.test/\".")); ServerResponse(200, Headers::kSend, "; rel=web-app-manifest", kNoResponseBody, net::OK); } TEST_F(PaymentMethodManifestDownloaderTest, - NoManifestRellInHttpLinkHeaderButWithResponseBodyIsSuccess) { - EXPECT_CALL(*this, OnManifestDownload(_, "response body", kNoError)); + NoManifestRellInHttpLinkHeaderButWithResponseBodyIsFailure) { + EXPECT_CALL(*this, + OnManifestDownload( + _, kNoContent, + "No \"Link: rel=payment-method-manifest\" HTTP header found " + "at \"https://bobpay.test/\".")); ServerResponse(200, Headers::kSend, "; rel=web-app-manifest", "response body", net::OK); @@ -282,14 +308,26 @@ TEST_F(PaymentMethodManifestDownloaderTest, EmptySecondResponseIsFailure) { } TEST_F(PaymentMethodManifestDownloaderTest, - SecondResponseWithoutHeadersIsFailure) { + SecondResponseWithoutHeadersButWithContentIsSuccess) { + ServerResponse(200, Headers::kSend, + "; rel=payment-method-manifest", + kNoResponseBody, net::OK); + + EXPECT_CALL(*this, OnManifestDownload(_, "response content", kNoError)); + + ServerResponse(200, Headers::kOmit, kNoLinkHeader, "response content", + net::OK); +} + +TEST_F(PaymentMethodManifestDownloaderTest, + SecondResponseWithoutContentIsFailure) { ServerResponse(200, Headers::kSend, "; rel=payment-method-manifest", kNoResponseBody, net::OK); EXPECT_CALL(*this, OnManifestDownload(_, kNoContent, - "Unable to download payment manifest " + "No content found in payment manifest " "\"https://bobpay.test/manifest.json\".")); ServerResponse(200, Headers::kOmit, kNoLinkHeader, kNoResponseBody, net::OK); @@ -560,6 +598,164 @@ TEST_F(PaymentMethodManifestDownloaderTest, NotAllowCrossSiteRedirects) { ServerRedirect(301, GURL("https://alicepay.test")); } +// Variant of PaymentMethodManifestDownloaderTest covering the logic when +// kPaymentHandlerRequireLinkHeader is set to false. +class PaymentMethodManifestDownloaderLinkHeaderNotRequiredTest + : public PaymentManifestDownloaderTestBase { + public: + PaymentMethodManifestDownloaderLinkHeaderNotRequiredTest() { + scoped_feature_list_.InitAndDisableFeature( + features::kPaymentHandlerRequireLinkHeader); + InitDownloader(); + downloader_->DownloadPaymentMethodManifest( + url::Origin::Create(GURL("https://chromium.org")), test_url_, + base::BindOnce(&PaymentManifestDownloaderTestBase::OnManifestDownload, + base::Unretained(this))); + } + + // Simulates two responses for payment method manifest download: + // 1) Only HTTP header without the response body content, responding to the + // initial HEAD request. + // 2) Both HTTP header and the response body content, for the subsequent GET + // request. + void ServerHeaderAndFallbackResponse(int response_code, + Headers send_headers, + absl::optional link_header, + const std::string& response_body, + int net_error) { + ServerResponse(response_code, send_headers, link_header, kNoResponseBody, + net_error); + ServerResponse(response_code, send_headers, link_header, response_body, + net_error); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; +TEST_F(PaymentMethodManifestDownloaderLinkHeaderNotRequiredTest, + NoHttpHeadersAndEmptyResponseBodyIsFailure) { + EXPECT_CALL(*this, + OnManifestDownload( + _, kNoContent, + "No content and no \"Link: rel=payment-method-manifest\" " + "HTTP header found at \"https://bobpay.test/\".")); + ServerHeaderAndFallbackResponse(200, Headers::kOmit, kNoLinkHeader, + kNoResponseBody, net::OK); +} + +TEST_F(PaymentMethodManifestDownloaderLinkHeaderNotRequiredTest, + NoHttpHeadersButWithResponseBodyIsSuccess) { + EXPECT_CALL(*this, OnManifestDownload(_, "response body", kNoError)); + + ServerHeaderAndFallbackResponse(200, Headers::kOmit, kNoLinkHeader, + "response body", net::OK); +} + +TEST_F(PaymentMethodManifestDownloaderLinkHeaderNotRequiredTest, + EmptyHttpHeaderAndEmptyResponseBodyIsFailure) { + EXPECT_CALL( + *this, OnManifestDownload( + _, kNoContent, + "No content and no \"Link: rel=payment-method-manifest\" HTTP " + "header found at \"https://bobpay.test/\".")); + + ServerHeaderAndFallbackResponse(200, Headers::kSend, kNoLinkHeader, + kNoResponseBody, net::OK); +} + +TEST_F(PaymentMethodManifestDownloaderLinkHeaderNotRequiredTest, + EmptyHttpHeaderButWithResponseBodyIsSuccess) { + EXPECT_CALL(*this, OnManifestDownload(_, "response content", kNoError)); + + ServerHeaderAndFallbackResponse(200, Headers::kSend, kNoLinkHeader, + "response content", net::OK); +} + +TEST_F(PaymentMethodManifestDownloaderLinkHeaderNotRequiredTest, + EmptyHttpLinkHeaderWithoutResponseBodyIsFailure) { + EXPECT_CALL(*this, + OnManifestDownload( + _, kNoContent, + "No content and no \"Link: rel=payment-method-manifest\" " + "HTTP header found at \"https://bobpay.test/\".")); + + ServerHeaderAndFallbackResponse(200, Headers::kSend, kEmptyLinkHeader, + kNoResponseBody, net::OK); +} + +TEST_F(PaymentMethodManifestDownloaderLinkHeaderNotRequiredTest, + EmptyHttpLinkHeaderButWithResponseBodyIsSuccess) { + EXPECT_CALL(*this, OnManifestDownload(_, "response body", kNoError)); + + + ServerHeaderAndFallbackResponse(200, Headers::kSend, kEmptyLinkHeader, + "response body", net::OK); +} + +TEST_F(PaymentMethodManifestDownloaderLinkHeaderNotRequiredTest, + NoRelInHttpLinkHeaderAndNoResponseBodyIsFailure) { + EXPECT_CALL(*this, + OnManifestDownload( + _, std::string(), + "No content and no \"Link: rel=payment-method-manifest\" " + "HTTP header found at \"https://bobpay.test/\".")); + + ServerHeaderAndFallbackResponse(200, Headers::kSend, "", + kNoResponseBody, net::OK); +} + +TEST_F(PaymentMethodManifestDownloaderLinkHeaderNotRequiredTest, + NoUrlInHttpLinkHeaderButWithResponseBodyIsSuccess) { + EXPECT_CALL(*this, OnManifestDownload(_, "response body", kNoError)); + + ServerHeaderAndFallbackResponse(200, Headers::kSend, + "rel=payment-method-manifest", + "response body", net::OK); +} + +TEST_F(PaymentMethodManifestDownloaderLinkHeaderNotRequiredTest, + NoManifestRellInHttpLinkHeaderAndNoResponseBodyIsFailure) { + EXPECT_CALL(*this, + OnManifestDownload( + _, kNoContent, + "No content and no \"Link: rel=payment-method-manifest\" " + "HTTP header found at \"https://bobpay.test/\".")); + + ServerHeaderAndFallbackResponse(200, Headers::kSend, + "; rel=web-app-manifest", + kNoResponseBody, net::OK); +} + +TEST_F(PaymentMethodManifestDownloaderLinkHeaderNotRequiredTest, + NoManifestRellInHttpLinkHeaderButWithResponseBodyIsSuccess) { + EXPECT_CALL(*this, OnManifestDownload(_, "response body", kNoError)); + + ServerHeaderAndFallbackResponse(200, Headers::kSend, + "; rel=web-app-manifest", + "response body", net::OK); +} + +TEST_F(PaymentMethodManifestDownloaderLinkHeaderNotRequiredTest, + NoRelInHttpLinkHeaderButWithResponseBodyIsSuccess) { + EXPECT_CALL(*this, OnManifestDownload(_, "response body", kNoError)); + + ServerHeaderAndFallbackResponse(200, Headers::kSend, "", + "response body", net::OK); +} + +TEST_F(PaymentMethodManifestDownloaderLinkHeaderNotRequiredTest, + NoUrlInHttpLinkHeaderAndNoResponseBodyIsFailure) { + EXPECT_CALL(*this, + OnManifestDownload( + _, kNoContent, + "No content and no \"Link: rel=payment-method-manifest\" " + "HTTP header found at \"https://bobpay.test/\".")); + + ServerHeaderAndFallbackResponse(200, Headers::kSend, + "rel=payment-method-manifest", + kNoResponseBody, net::OK); +} + class WebAppManifestDownloaderTest : public PaymentManifestDownloaderTestBase { public: WebAppManifestDownloaderTest() {