blob: 6cf7c765bece6c9fb6b9114dd376f93a646ec470 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/subresource_redirect/origin_robots_rules.h"
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "net/base/escape.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "services/network/test/test_url_loader_factory.h"
#include "services/network/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
namespace subresource_redirect {
constexpr char kLitePagesURL[] = "https://litepages.googlezip.net/robots?u=";
constexpr char kFooOrigin[] = "https://foo.com/";
constexpr char kTestResponse[] = "TEST RESPONSE";
class RobotsRulesFetcherState {
public:
OriginRobotsRules::RobotsRulesReceivedCallback
GetRobotsRulesReceivedCallback() {
return base::BindOnce(&RobotsRulesFetcherState::OnRobotsRulesReceived,
weak_ptr_factory_.GetWeakPtr());
}
OriginRobotsRules::NotifyResponseErrorCallback GetResponseErrorCallback() {
return base::BindOnce(&RobotsRulesFetcherState::OnResponseErrorReceived,
weak_ptr_factory_.GetWeakPtr());
}
private:
friend class SubresourceRedirectOriginRobotsRulesTest;
void OnResponseErrorReceived(int response_code, base::TimeDelta retry_after) {
EXPECT_FALSE(response_error_received_.has_value());
response_error_received_ =
std::pair<int, base::TimeDelta>(response_code, retry_after);
}
void OnRobotsRulesReceived(base::Optional<std::string> rules) {
robots_rules_received_.push_back(rules);
}
base::Optional<std::pair<int, base::TimeDelta>> response_error_received_;
std::vector<base::Optional<std::string>> robots_rules_received_;
base::WeakPtrFactory<RobotsRulesFetcherState> weak_ptr_factory_{this};
};
class SubresourceRedirectOriginRobotsRulesTest : public testing::Test {
public:
SubresourceRedirectOriginRobotsRulesTest()
: task_environment_(base::test::TaskEnvironment::MainThreadType::UI),
shared_url_loader_factory_(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_)) {
scoped_feature_list_.InitWithFeaturesAndParameters(
{{blink::features::kSubresourceRedirect,
{{"enable_login_robots_based_compression", "true"},
{"enable_public_image_hints_based_compression", "false"}}}},
{});
}
void CreateRobotsRulesFetcher(const std::string& origin) {
rules_fetcher_state_ = std::make_unique<RobotsRulesFetcherState>();
origin_robots_rules_ = std::make_unique<OriginRobotsRules>(
shared_url_loader_factory_, url::SchemeHostPort(GURL(origin)),
rules_fetcher_state_->GetResponseErrorCallback());
}
void GetRobotsRules() {
origin_robots_rules_->GetRobotsRules(
rules_fetcher_state_->GetRobotsRulesReceivedCallback());
}
bool SimulateResponse(const std::string& lite_pages_url,
std::string robots_origin,
const std::string& content,
int net_error = net::OK,
net::HttpStatusCode http_status = net::HTTP_OK,
bool is_cache_hit = false,
const std::string& retry_after = "") {
GURL url(lite_pages_url +
net::EscapeQueryParamValue(robots_origin + "robots.txt", true));
network::mojom::URLResponseHeadPtr head =
network::CreateURLResponseHead(http_status);
head->was_fetched_via_cache = is_cache_hit;
head->mime_type = "text/html";
head->headers->SetHeader("Retry-After", retry_after);
network::URLLoaderCompletionStatus status;
status.error_code = net_error;
status.decoded_body_length = content.size();
return test_url_loader_factory_.SimulateResponseForPendingRequest(
url, status, std::move(head), content);
}
base::Optional<std::pair<int, base::TimeDelta>> GetResponseErrorReceived() {
return rules_fetcher_state_->response_error_received_;
}
std::vector<base::Optional<std::string>> GetRobotsRulesReceived() {
return rules_fetcher_state_->robots_rules_received_;
}
protected:
void RunUntilIdle() {
task_environment_.RunUntilIdle();
base::RunLoop().RunUntilIdle();
}
base::test::ScopedFeatureList scoped_feature_list_;
base::test::TaskEnvironment task_environment_;
base::HistogramTester histogram_tester_;
std::unique_ptr<OriginRobotsRules> origin_robots_rules_;
std::unique_ptr<RobotsRulesFetcherState> rules_fetcher_state_;
scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
network::TestURLLoaderFactory test_url_loader_factory_;
};
TEST_F(SubresourceRedirectOriginRobotsRulesTest, TestSuccessfulResponse) {
CreateRobotsRulesFetcher(kFooOrigin);
GetRobotsRules();
// No robots rules received yet.
EXPECT_EQ(0ULL, GetRobotsRulesReceived().size());
EXPECT_TRUE(SimulateResponse(kLitePagesURL, kFooOrigin, kTestResponse));
EXPECT_EQ(base::nullopt, GetResponseErrorReceived());
histogram_tester_.ExpectUniqueSample(
"SubresourceRedirect.RobotsRulesFetcher.CacheHit", false, 1);
histogram_tester_.ExpectUniqueSample(
"SubresourceRedirect.RobotsRulesFetcher.NetErrorCode", 0, 1);
histogram_tester_.ExpectUniqueSample(
"SubresourceRedirect.RobotsRulesFetcher.ResponseCode", net::HTTP_OK, 1);
EXPECT_THAT(GetRobotsRulesReceived(),
testing::ElementsAre(base::Optional<std::string>(kTestResponse)));
// Subsequent calls will return the response immediately.
GetRobotsRules();
GetRobotsRules();
EXPECT_THAT(GetRobotsRulesReceived(),
testing::ElementsAre(base::Optional<std::string>(kTestResponse),
base::Optional<std::string>(kTestResponse),
base::Optional<std::string>(kTestResponse)));
EXPECT_EQ(base::nullopt, GetResponseErrorReceived());
}
TEST_F(SubresourceRedirectOriginRobotsRulesTest, TestSuccessfulCachedResponse) {
CreateRobotsRulesFetcher(kFooOrigin);
GetRobotsRules();
EXPECT_TRUE(SimulateResponse(kLitePagesURL, kFooOrigin, kTestResponse,
net::OK, net::HTTP_OK, true /*is_cache_hit*/));
RunUntilIdle();
histogram_tester_.ExpectUniqueSample(
"SubresourceRedirect.RobotsRulesFetcher.CacheHit", true, 1);
histogram_tester_.ExpectUniqueSample(
"SubresourceRedirect.RobotsRulesFetcher.NetErrorCode", 0, 1);
histogram_tester_.ExpectUniqueSample(
"SubresourceRedirect.RobotsRulesFetcher.ResponseCode", net::HTTP_OK, 1);
EXPECT_EQ(base::nullopt, GetResponseErrorReceived());
EXPECT_THAT(GetRobotsRulesReceived(),
testing::ElementsAre(base::Optional<std::string>(kTestResponse)));
GetRobotsRules();
GetRobotsRules();
EXPECT_THAT(GetRobotsRulesReceived(),
testing::ElementsAre(base::Optional<std::string>(kTestResponse),
base::Optional<std::string>(kTestResponse),
base::Optional<std::string>(kTestResponse)));
EXPECT_EQ(base::nullopt, GetResponseErrorReceived());
}
TEST_F(SubresourceRedirectOriginRobotsRulesTest, TestFailedResponse) {
CreateRobotsRulesFetcher(kFooOrigin);
GetRobotsRules();
EXPECT_TRUE(SimulateResponse(kLitePagesURL, kFooOrigin, kTestResponse,
net::OK, net::HTTP_INTERNAL_SERVER_ERROR));
RunUntilIdle();
histogram_tester_.ExpectUniqueSample(
"SubresourceRedirect.RobotsRulesFetcher.CacheHit", false, 1);
histogram_tester_.ExpectTotalCount(
"SubresourceRedirect.RobotsRulesFetcher.NetErrorCode", 1);
histogram_tester_.ExpectUniqueSample(
"SubresourceRedirect.RobotsRulesFetcher.ResponseCode",
net::HTTP_INTERNAL_SERVER_ERROR, 1);
EXPECT_THAT(
*GetResponseErrorReceived(),
testing::Pair(net::HTTP_INTERNAL_SERVER_ERROR, base::TimeDelta()));
EXPECT_THAT(GetRobotsRulesReceived(), testing::ElementsAre(base::nullopt));
// Subsequent calls will return the response immediately.
GetRobotsRules();
GetRobotsRules();
EXPECT_THAT(
GetRobotsRulesReceived(),
testing::ElementsAre(base::nullopt, base::nullopt, base::nullopt));
}
TEST_F(SubresourceRedirectOriginRobotsRulesTest,
TestFailedResponseWithRetryAfter) {
CreateRobotsRulesFetcher(kFooOrigin);
GetRobotsRules();
EXPECT_TRUE(SimulateResponse(kLitePagesURL, kFooOrigin, kTestResponse,
net::OK, net::HTTP_INTERNAL_SERVER_ERROR,
false /*is_cache_hit*/, "120"));
RunUntilIdle();
histogram_tester_.ExpectUniqueSample(
"SubresourceRedirect.RobotsRulesFetcher.CacheHit", false, 1);
histogram_tester_.ExpectTotalCount(
"SubresourceRedirect.RobotsRulesFetcher.NetErrorCode", 1);
histogram_tester_.ExpectUniqueSample(
"SubresourceRedirect.RobotsRulesFetcher.ResponseCode",
net::HTTP_INTERNAL_SERVER_ERROR, 1);
EXPECT_THAT(*GetResponseErrorReceived(),
testing::Pair(net::HTTP_INTERNAL_SERVER_ERROR,
base::TimeDelta::FromSeconds(120)));
EXPECT_THAT(GetRobotsRulesReceived(), testing::ElementsAre(base::nullopt));
// Subsequent calls will return the response immediately.
GetRobotsRules();
GetRobotsRules();
EXPECT_THAT(
GetRobotsRulesReceived(),
testing::ElementsAre(base::nullopt, base::nullopt, base::nullopt));
}
TEST_F(SubresourceRedirectOriginRobotsRulesTest, TestNetErrorFailedResponse) {
CreateRobotsRulesFetcher(kFooOrigin);
GetRobotsRules();
EXPECT_TRUE(SimulateResponse(kLitePagesURL, kFooOrigin, kTestResponse,
net::ERR_ADDRESS_UNREACHABLE));
RunUntilIdle();
histogram_tester_.ExpectUniqueSample(
"SubresourceRedirect.RobotsRulesFetcher.CacheHit", false, 1);
histogram_tester_.ExpectUniqueSample(
"SubresourceRedirect.RobotsRulesFetcher.NetErrorCode",
-net::ERR_ADDRESS_UNREACHABLE, 1);
histogram_tester_.ExpectTotalCount(
"SubresourceRedirect.RobotsRulesFetcher.ResponseCode", 0);
EXPECT_EQ(base::nullopt, GetResponseErrorReceived());
EXPECT_THAT(GetRobotsRulesReceived(), testing::ElementsAre(base::nullopt));
// Subsequent calls will return the response immediately.
GetRobotsRules();
GetRobotsRules();
EXPECT_THAT(
GetRobotsRulesReceived(),
testing::ElementsAre(base::nullopt, base::nullopt, base::nullopt));
}
} // namespace subresource_redirect