| // Copyright 2017 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 "net/reporting/reporting_header_parser.h" |
| |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/json/json_reader.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/test/simple_test_tick_clock.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "net/reporting/mock_persistent_reporting_store.h" |
| #include "net/reporting/reporting_cache.h" |
| #include "net/reporting/reporting_endpoint.h" |
| #include "net/reporting/reporting_test_util.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| |
| namespace net { |
| namespace { |
| |
| using CommandType = MockPersistentReportingStore::Command::Type; |
| |
| // This test is parametrized on a boolean that represents whether to use a |
| // MockPersistentReportingStore. |
| class ReportingHeaderParserTest : public ReportingTestBase, |
| public ::testing::WithParamInterface<bool> { |
| protected: |
| ReportingHeaderParserTest() : ReportingTestBase() { |
| ReportingPolicy policy; |
| policy.max_endpoints_per_origin = 10; |
| policy.max_endpoint_count = 20; |
| UsePolicy(policy); |
| |
| if (GetParam()) |
| store_ = std::make_unique<MockPersistentReportingStore>(); |
| else |
| store_ = nullptr; |
| UseStore(store_.get()); |
| } |
| |
| ~ReportingHeaderParserTest() override = default; |
| |
| void SetUp() override { |
| // All ReportingCache methods assume that the store has been initialized. |
| if (mock_store()) { |
| mock_store()->LoadReportingClients( |
| base::BindOnce(&ReportingCache::AddClientsLoadedFromStore, |
| base::Unretained(cache()))); |
| mock_store()->FinishLoading(true); |
| } |
| } |
| |
| MockPersistentReportingStore* mock_store() { return store_.get(); } |
| |
| ReportingEndpointGroup MakeEndpointGroup( |
| const std::string& name, |
| const std::vector<ReportingEndpoint::EndpointInfo>& endpoints, |
| OriginSubdomains include_subdomains = OriginSubdomains::DEFAULT, |
| base::TimeDelta ttl = base::TimeDelta::FromDays(1), |
| url::Origin origin = url::Origin()) { |
| ReportingEndpointGroupKey group_key(kNik_ /* unused */, |
| url::Origin() /* unused */, name); |
| ReportingEndpointGroup group; |
| group.group_key = group_key; |
| group.include_subdomains = include_subdomains; |
| group.ttl = ttl; |
| group.endpoints = std::move(endpoints); |
| return group; |
| } |
| |
| // Constructs a string which would represent a single group in a Report-To |
| // header. If |group_name| is an empty string, the group name will be omitted |
| // (and thus default to "default" when parsed). Setting |omit_defaults| omits |
| // the priority, weight, and include_subdomains fields if they are default, |
| // otherwise they are spelled out fully. |
| std::string ConstructHeaderGroupString(const ReportingEndpointGroup& group, |
| bool omit_defaults = true) { |
| std::ostringstream s; |
| s << "{ "; |
| |
| if (!group.group_key.group_name.empty()) { |
| s << "\"group\": \"" << group.group_key.group_name << "\", "; |
| } |
| |
| s << "\"max_age\": " << group.ttl.InSeconds() << ", "; |
| |
| if (group.include_subdomains != OriginSubdomains::DEFAULT) { |
| s << "\"include_subdomains\": true, "; |
| } else if (!omit_defaults) { |
| s << "\"include_subdomains\": false, "; |
| } |
| |
| s << "\"endpoints\": ["; |
| for (const ReportingEndpoint::EndpointInfo& endpoint_info : |
| group.endpoints) { |
| s << "{ "; |
| s << "\"url\": \"" << endpoint_info.url.spec() << "\""; |
| |
| if (!omit_defaults || |
| endpoint_info.priority != |
| ReportingEndpoint::EndpointInfo::kDefaultPriority) { |
| s << ", \"priority\": " << endpoint_info.priority; |
| } |
| |
| if (!omit_defaults || |
| endpoint_info.weight != |
| ReportingEndpoint::EndpointInfo::kDefaultWeight) { |
| s << ", \"weight\": " << endpoint_info.weight; |
| } |
| |
| s << " }, "; |
| } |
| if (!group.endpoints.empty()) |
| s.seekp(-2, s.cur); // Overwrite trailing comma and space. |
| s << "]"; |
| |
| s << " }"; |
| |
| return s.str(); |
| } |
| |
| void ParseHeader(const NetworkIsolationKey& network_isolation_key, |
| const GURL& url, |
| const std::string& json) { |
| std::unique_ptr<base::Value> value = |
| base::JSONReader::ReadDeprecated("[" + json + "]"); |
| if (value) { |
| ReportingHeaderParser::ParseHeader(context(), network_isolation_key, url, |
| std::move(value)); |
| } |
| } |
| |
| const GURL kUrl1_ = GURL("https://origin1.test/path"); |
| const url::Origin kOrigin1_ = url::Origin::Create(kUrl1_); |
| const GURL kUrl2_ = GURL("https://origin2.test/path"); |
| const url::Origin kOrigin2_ = url::Origin::Create(kUrl2_); |
| const NetworkIsolationKey kNik_; |
| const NetworkIsolationKey kOtherNik_ = |
| NetworkIsolationKey(kOrigin1_, kOrigin2_); |
| const GURL kUrlEtld_ = GURL("https://co.uk/foo.html/"); |
| const url::Origin kOriginEtld_ = url::Origin::Create(kUrlEtld_); |
| const GURL kEndpoint1_ = GURL("https://endpoint1.test/"); |
| const GURL kEndpoint2_ = GURL("https://endpoint2.test/"); |
| const GURL kEndpoint3_ = GURL("https://endpoint3.test/"); |
| const GURL kEndpointPathAbsolute_ = |
| GURL("https://origin1.test/path-absolute-url"); |
| const std::string kGroup1_ = "group1"; |
| const std::string kGroup2_ = "group2"; |
| // There are 2^3 = 8 of these to test the different combinations of matching |
| // vs mismatching NIK, origin, and group. |
| const ReportingEndpointGroupKey kGroupKey11_ = |
| ReportingEndpointGroupKey(kNik_, kOrigin1_, kGroup1_); |
| const ReportingEndpointGroupKey kGroupKey21_ = |
| ReportingEndpointGroupKey(kNik_, kOrigin2_, kGroup1_); |
| const ReportingEndpointGroupKey kGroupKey12_ = |
| ReportingEndpointGroupKey(kNik_, kOrigin1_, kGroup2_); |
| const ReportingEndpointGroupKey kGroupKey22_ = |
| ReportingEndpointGroupKey(kNik_, kOrigin2_, kGroup2_); |
| |
| private: |
| std::unique_ptr<MockPersistentReportingStore> store_; |
| }; |
| |
| // TODO(juliatuttle): Ideally these tests should be expecting that JSON parsing |
| // (and therefore header parsing) may happen asynchronously, but the entire |
| // pipeline is also tested by NetworkErrorLoggingEndToEndTest. |
| |
| TEST_P(ReportingHeaderParserTest, Invalid) { |
| static const struct { |
| const char* header_value; |
| const char* description; |
| } kInvalidHeaderTestCases[] = { |
| {"{\"max_age\":1, \"endpoints\": [{}]}", "missing url"}, |
| {"{\"max_age\":1, \"endpoints\": [{\"url\":0}]}", "non-string url"}, |
| {"{\"max_age\":1, \"endpoints\": [{\"url\":\"//scheme/relative\"}]}", |
| "scheme-relative url"}, |
| {"{\"max_age\":1, \"endpoints\": [{\"url\":\"relative/path\"}]}", |
| "path relative url"}, |
| {"{\"max_age\":1, \"endpoints\": [{\"url\":\"http://insecure/\"}]}", |
| "insecure url"}, |
| {"{\"endpoints\": [{\"url\":\"https://endpoint/\"}]}", "missing max_age"}, |
| {"{\"max_age\":\"\", \"endpoints\": [{\"url\":\"https://endpoint/\"}]}", |
| "non-integer max_age"}, |
| {"{\"max_age\":-1, \"endpoints\": [{\"url\":\"https://endpoint/\"}]}", |
| "negative max_age"}, |
| {"{\"max_age\":1, \"group\":0, " |
| "\"endpoints\": [{\"url\":\"https://endpoint/\"}]}", |
| "non-string group"}, |
| |
| // Note that a non-boolean include_subdomains field is *not* invalid, per |
| // the spec. |
| |
| // Priority should be a nonnegative integer. |
| {"{\"max_age\":1, " |
| "\"endpoints\": [{\"url\":\"https://endpoint/\",\"priority\":\"\"}]}", |
| "non-integer priority"}, |
| {"{\"max_age\":1, " |
| "\"endpoints\": [{\"url\":\"https://endpoint/\",\"priority\":-1}]}", |
| "negative priority"}, |
| |
| // Weight should be a non-negative integer. |
| {"{\"max_age\":1, " |
| "\"endpoints\": [{\"url\":\"https://endpoint/\",\"weight\":\"\"}]}", |
| "non-integer weight"}, |
| {"{\"max_age\":1, " |
| "\"endpoints\": [{\"url\":\"https://endpoint/\",\"weight\":-1}]}", |
| "negative weight"}, |
| |
| {"[{\"max_age\":1, \"endpoints\": [{\"url\":\"https://a/\"}]}," |
| "{\"max_age\":1, \"endpoints\": [{\"url\":\"https://b/\"}]}]", |
| "wrapped in list"}}; |
| |
| for (size_t i = 0; i < base::size(kInvalidHeaderTestCases); ++i) { |
| auto& test_case = kInvalidHeaderTestCases[i]; |
| ParseHeader(kNik_, kUrl1_, test_case.header_value); |
| |
| EXPECT_EQ(0u, cache()->GetEndpointCount()) |
| << "Invalid Report-To header (" << test_case.description << ": \"" |
| << test_case.header_value << "\") parsed as valid."; |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(0, mock_store()->StoredEndpointsCount()); |
| EXPECT_EQ(0, mock_store()->StoredEndpointGroupsCount()); |
| } |
| } |
| } |
| |
| TEST_P(ReportingHeaderParserTest, Basic) { |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}}; |
| |
| std::string header = |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints)); |
| |
| ParseHeader(kNik_, kUrl1_, header); |
| EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_)); |
| EXPECT_EQ(1u, cache()->GetEndpointCount()); |
| ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey11_, kEndpoint1_); |
| ASSERT_TRUE(endpoint); |
| EXPECT_EQ(kOrigin1_, endpoint.group_key.origin); |
| EXPECT_EQ(kGroup1_, endpoint.group_key.group_name); |
| EXPECT_EQ(kEndpoint1_, endpoint.info.url); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| endpoint.info.priority); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight, |
| endpoint.info.weight); |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(1, mock_store()->StoredEndpointsCount()); |
| EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount()); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey11_, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey11_); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| } |
| |
| TEST_P(ReportingHeaderParserTest, PathAbsoluteURLEndpoint) { |
| std::string header = |
| "{\"group\": \"group1\", \"max_age\":1, \"endpoints\": " |
| "[{\"url\":\"/path-absolute-url\"}]}"; |
| |
| ParseHeader(kNik_, kUrl1_, header); |
| EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_)); |
| EXPECT_EQ(1u, cache()->GetEndpointCount()); |
| ReportingEndpoint endpoint = |
| FindEndpointInCache(kGroupKey11_, kEndpointPathAbsolute_); |
| ASSERT_TRUE(endpoint); |
| EXPECT_EQ(kOrigin1_, endpoint.group_key.origin); |
| EXPECT_EQ(kGroup1_, endpoint.group_key.group_name); |
| EXPECT_EQ(kEndpointPathAbsolute_, endpoint.info.url); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| endpoint.info.priority); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight, |
| endpoint.info.weight); |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(1, mock_store()->StoredEndpointsCount()); |
| EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount()); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back( |
| CommandType::ADD_REPORTING_ENDPOINT, |
| ReportingEndpoint(kGroupKey11_, ReportingEndpoint::EndpointInfo{ |
| kEndpointPathAbsolute_})); |
| expected_commands.emplace_back( |
| CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| CachedReportingEndpointGroup( |
| kGroupKey11_, OriginSubdomains::DEFAULT /* irrelevant */, |
| base::Time() /* irrelevant */, base::Time() /* irrelevant */)); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| } |
| |
| TEST_P(ReportingHeaderParserTest, OmittedGroupName) { |
| ReportingEndpointGroupKey kGroupKey(kNik_, kOrigin1_, "default"); |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}}; |
| std::string header = |
| ConstructHeaderGroupString(MakeEndpointGroup(std::string(), endpoints)); |
| |
| ParseHeader(kNik_, kUrl1_, header); |
| EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_TRUE(EndpointGroupExistsInCache(kGroupKey, OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_)); |
| EXPECT_EQ(1u, cache()->GetEndpointCount()); |
| ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey, kEndpoint1_); |
| ASSERT_TRUE(endpoint); |
| EXPECT_EQ(kOrigin1_, endpoint.group_key.origin); |
| EXPECT_EQ("default", endpoint.group_key.group_name); |
| EXPECT_EQ(kEndpoint1_, endpoint.info.url); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| endpoint.info.priority); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight, |
| endpoint.info.weight); |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(1, mock_store()->StoredEndpointsCount()); |
| EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount()); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| } |
| |
| TEST_P(ReportingHeaderParserTest, IncludeSubdomainsTrue) { |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}}; |
| |
| std::string header = ConstructHeaderGroupString( |
| MakeEndpointGroup(kGroup1_, endpoints, OriginSubdomains::INCLUDE)); |
| ParseHeader(kNik_, kUrl1_, header); |
| |
| EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::INCLUDE)); |
| EXPECT_EQ(1u, cache()->GetEndpointCount()); |
| EXPECT_TRUE(EndpointExistsInCache(kGroupKey11_, kEndpoint1_)); |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(1, mock_store()->StoredEndpointsCount()); |
| EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount()); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey11_, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey11_); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| } |
| |
| TEST_P(ReportingHeaderParserTest, IncludeSubdomainsFalse) { |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}}; |
| |
| std::string header = ConstructHeaderGroupString( |
| MakeEndpointGroup(kGroup1_, endpoints, OriginSubdomains::EXCLUDE), |
| false /* omit_defaults */); |
| ParseHeader(kNik_, kUrl1_, header); |
| |
| EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::EXCLUDE)); |
| EXPECT_EQ(1u, cache()->GetEndpointCount()); |
| EXPECT_TRUE(EndpointExistsInCache(kGroupKey11_, kEndpoint1_)); |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(1, mock_store()->StoredEndpointsCount()); |
| EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount()); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey11_, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey11_); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| } |
| |
| TEST_P(ReportingHeaderParserTest, IncludeSubdomainsEtldRejected) { |
| ReportingEndpointGroupKey kGroupKey(kNik_, kOriginEtld_, kGroup1_); |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}}; |
| |
| std::string header = ConstructHeaderGroupString( |
| MakeEndpointGroup(kGroup1_, endpoints, OriginSubdomains::INCLUDE)); |
| ParseHeader(kNik_, kUrlEtld_, header); |
| |
| EXPECT_EQ(0u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_FALSE( |
| EndpointGroupExistsInCache(kGroupKey, OriginSubdomains::INCLUDE)); |
| EXPECT_EQ(0u, cache()->GetEndpointCount()); |
| EXPECT_FALSE(EndpointExistsInCache(kGroupKey, kEndpoint1_)); |
| } |
| |
| TEST_P(ReportingHeaderParserTest, NonIncludeSubdomainsEtldAccepted) { |
| ReportingEndpointGroupKey kGroupKey(kNik_, kOriginEtld_, kGroup1_); |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}}; |
| |
| std::string header = ConstructHeaderGroupString( |
| MakeEndpointGroup(kGroup1_, endpoints, OriginSubdomains::EXCLUDE)); |
| ParseHeader(kNik_, kUrlEtld_, header); |
| |
| EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_TRUE(EndpointGroupExistsInCache(kGroupKey, OriginSubdomains::EXCLUDE)); |
| EXPECT_EQ(1u, cache()->GetEndpointCount()); |
| EXPECT_TRUE(EndpointExistsInCache(kGroupKey, kEndpoint1_)); |
| } |
| |
| TEST_P(ReportingHeaderParserTest, IncludeSubdomainsNotBoolean) { |
| std::string header = |
| "{\"group\": \"" + kGroup1_ + |
| "\", " |
| "\"max_age\":86400, \"include_subdomains\": \"NotABoolean\", " |
| "\"endpoints\": [{\"url\":\"" + |
| kEndpoint1_.spec() + "\"}]}"; |
| ParseHeader(kNik_, kUrl1_, header); |
| |
| EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT)); |
| EXPECT_EQ(1u, cache()->GetEndpointCount()); |
| EXPECT_TRUE(EndpointExistsInCache(kGroupKey11_, kEndpoint1_)); |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(1, mock_store()->StoredEndpointsCount()); |
| EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount()); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey11_, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey11_); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| } |
| |
| TEST_P(ReportingHeaderParserTest, NonDefaultPriority) { |
| const int kNonDefaultPriority = 10; |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints = { |
| {kEndpoint1_, kNonDefaultPriority}}; |
| |
| std::string header = |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints)); |
| ParseHeader(kNik_, kUrl1_, header); |
| |
| EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT)); |
| EXPECT_EQ(1u, cache()->GetEndpointCount()); |
| ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey11_, kEndpoint1_); |
| ASSERT_TRUE(endpoint); |
| EXPECT_EQ(kNonDefaultPriority, endpoint.info.priority); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight, |
| endpoint.info.weight); |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(1, mock_store()->StoredEndpointsCount()); |
| EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount()); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey11_, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey11_); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| } |
| |
| TEST_P(ReportingHeaderParserTest, NonDefaultWeight) { |
| const int kNonDefaultWeight = 10; |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints = { |
| {kEndpoint1_, ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| kNonDefaultWeight}}; |
| |
| std::string header = |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints)); |
| ParseHeader(kNik_, kUrl1_, header); |
| |
| EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT)); |
| EXPECT_EQ(1u, cache()->GetEndpointCount()); |
| ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey11_, kEndpoint1_); |
| ASSERT_TRUE(endpoint); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| endpoint.info.priority); |
| EXPECT_EQ(kNonDefaultWeight, endpoint.info.weight); |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(1, mock_store()->StoredEndpointsCount()); |
| EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount()); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey11_, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey11_); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| } |
| |
| TEST_P(ReportingHeaderParserTest, MaxAge) { |
| const int kMaxAgeSecs = 100; |
| base::TimeDelta ttl = base::TimeDelta::FromSeconds(kMaxAgeSecs); |
| base::Time expires = clock()->Now() + ttl; |
| |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}}; |
| |
| std::string header = ConstructHeaderGroupString( |
| MakeEndpointGroup(kGroup1_, endpoints, OriginSubdomains::DEFAULT, ttl)); |
| |
| ParseHeader(kNik_, kUrl1_, header); |
| EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_TRUE(EndpointGroupExistsInCache(kGroupKey11_, |
| OriginSubdomains::DEFAULT, expires)); |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(1, mock_store()->StoredEndpointsCount()); |
| EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount()); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey11_, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey11_); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| } |
| |
| TEST_P(ReportingHeaderParserTest, MultipleEndpointsSameGroup) { |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}, |
| {kEndpoint2_}}; |
| std::string header = |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints)); |
| |
| ParseHeader(kNik_, kUrl1_, header); |
| EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_)); |
| EXPECT_EQ(2u, cache()->GetEndpointCount()); |
| ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey11_, kEndpoint1_); |
| ASSERT_TRUE(endpoint); |
| EXPECT_EQ(kOrigin1_, endpoint.group_key.origin); |
| EXPECT_EQ(kGroup1_, endpoint.group_key.group_name); |
| EXPECT_EQ(kEndpoint1_, endpoint.info.url); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| endpoint.info.priority); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight, |
| endpoint.info.weight); |
| |
| ReportingEndpoint endpoint2 = FindEndpointInCache(kGroupKey11_, kEndpoint2_); |
| ASSERT_TRUE(endpoint2); |
| EXPECT_EQ(kOrigin1_, endpoint2.group_key.origin); |
| EXPECT_EQ(kGroup1_, endpoint2.group_key.group_name); |
| EXPECT_EQ(kEndpoint2_, endpoint2.info.url); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| endpoint2.info.priority); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight, |
| endpoint2.info.weight); |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(2, mock_store()->StoredEndpointsCount()); |
| EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount()); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey11_, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey11_, kEndpoint2_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey11_); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| } |
| |
| TEST_P(ReportingHeaderParserTest, MultipleEndpointsDifferentGroups) { |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_}}; |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint1_}}; |
| std::string header = |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1)) + |
| ", " + |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup2_, endpoints2)); |
| |
| ParseHeader(kNik_, kUrl1_, header); |
| EXPECT_EQ(2u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey12_, OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_)); |
| |
| EXPECT_EQ(2u, cache()->GetEndpointCount()); |
| ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey11_, kEndpoint1_); |
| ASSERT_TRUE(endpoint); |
| EXPECT_EQ(kOrigin1_, endpoint.group_key.origin); |
| EXPECT_EQ(kGroup1_, endpoint.group_key.group_name); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| endpoint.info.priority); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight, |
| endpoint.info.weight); |
| |
| ReportingEndpoint endpoint2 = FindEndpointInCache(kGroupKey12_, kEndpoint1_); |
| ASSERT_TRUE(endpoint2); |
| EXPECT_EQ(kOrigin1_, endpoint2.group_key.origin); |
| EXPECT_EQ(kGroup2_, endpoint2.group_key.group_name); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| endpoint2.info.priority); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight, |
| endpoint2.info.weight); |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(2, mock_store()->StoredEndpointsCount()); |
| EXPECT_EQ(2, mock_store()->StoredEndpointGroupsCount()); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey11_, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey12_, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey11_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey12_); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| } |
| |
| TEST_P(ReportingHeaderParserTest, MultipleHeadersFromDifferentOrigins) { |
| // First origin sets a header with two endpoints in the same group. |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_}, |
| {kEndpoint2_}}; |
| std::string header1 = |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1)); |
| ParseHeader(kNik_, kUrl1_, header1); |
| |
| // Second origin has two endpoint groups. |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint1_}}; |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints3 = {{kEndpoint2_}}; |
| std::string header2 = |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints2)) + |
| ", " + |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup2_, endpoints3)); |
| ParseHeader(kNik_, kUrl2_, header2); |
| |
| EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_)); |
| EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin2_)); |
| |
| EXPECT_EQ(3u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey21_, OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey22_, OriginSubdomains::DEFAULT)); |
| |
| EXPECT_EQ(4u, cache()->GetEndpointCount()); |
| EXPECT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint1_)); |
| EXPECT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint2_)); |
| EXPECT_TRUE(FindEndpointInCache(kGroupKey21_, kEndpoint1_)); |
| EXPECT_TRUE(FindEndpointInCache(kGroupKey22_, kEndpoint2_)); |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(4, mock_store()->StoredEndpointsCount()); |
| EXPECT_EQ(3, mock_store()->StoredEndpointGroupsCount()); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey11_, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey11_, kEndpoint2_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey21_, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey22_, kEndpoint2_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey11_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey21_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey22_); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| } |
| |
| // Test that each combination of NIK, origin, and group name is considered |
| // distinct. |
| // See also: ReportingCacheTest.ClientsKeyedByEndpointGroupKey |
| TEST_P(ReportingHeaderParserTest, EndpointGroupKey) { |
| // Raise the endpoint limits for this test. |
| ReportingPolicy policy; |
| policy.max_endpoints_per_origin = 5; // This test should use 4. |
| policy.max_endpoint_count = 20; // This test should use 16. |
| UsePolicy(policy); |
| |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_}, |
| {kEndpoint2_}}; |
| std::string header1 = |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1)) + |
| ", " + |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup2_, endpoints1)); |
| |
| const ReportingEndpointGroupKey kOtherGroupKey11 = |
| ReportingEndpointGroupKey(kOtherNik_, kOrigin1_, kGroup1_); |
| const ReportingEndpointGroupKey kOtherGroupKey21 = |
| ReportingEndpointGroupKey(kOtherNik_, kOrigin2_, kGroup1_); |
| const ReportingEndpointGroupKey kOtherGroupKey12 = |
| ReportingEndpointGroupKey(kOtherNik_, kOrigin1_, kGroup2_); |
| const ReportingEndpointGroupKey kOtherGroupKey22 = |
| ReportingEndpointGroupKey(kOtherNik_, kOrigin2_, kGroup2_); |
| |
| const struct { |
| NetworkIsolationKey network_isolation_key; |
| GURL url; |
| ReportingEndpointGroupKey group1_key; |
| ReportingEndpointGroupKey group2_key; |
| } kHeaderSources[] = { |
| {kNik_, kUrl1_, kGroupKey11_, kGroupKey12_}, |
| {kNik_, kUrl2_, kGroupKey21_, kGroupKey22_}, |
| {kOtherNik_, kUrl1_, kOtherGroupKey11, kOtherGroupKey12}, |
| {kOtherNik_, kUrl2_, kOtherGroupKey21, kOtherGroupKey22}, |
| }; |
| |
| size_t endpoint_group_count = 0u; |
| size_t endpoint_count = 0u; |
| MockPersistentReportingStore::CommandList expected_commands; |
| |
| // Set 2 endpoints in each of 2 groups for each of 2x2 combinations of |
| // (NIK, origin). |
| for (const auto& source : kHeaderSources) { |
| // Verify pre-parsing state |
| EXPECT_FALSE(FindEndpointInCache(source.group1_key, kEndpoint1_)); |
| EXPECT_FALSE(FindEndpointInCache(source.group1_key, kEndpoint2_)); |
| EXPECT_FALSE(FindEndpointInCache(source.group2_key, kEndpoint1_)); |
| EXPECT_FALSE(FindEndpointInCache(source.group2_key, kEndpoint2_)); |
| EXPECT_FALSE(EndpointGroupExistsInCache(source.group1_key, |
| OriginSubdomains::DEFAULT)); |
| EXPECT_FALSE(EndpointGroupExistsInCache(source.group2_key, |
| OriginSubdomains::DEFAULT)); |
| |
| ParseHeader(source.network_isolation_key, source.url, header1); |
| endpoint_group_count += 2u; |
| endpoint_count += 4u; |
| EXPECT_EQ(endpoint_group_count, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_EQ(endpoint_count, cache()->GetEndpointCount()); |
| |
| // Verify post-parsing state |
| EXPECT_TRUE(FindEndpointInCache(source.group1_key, kEndpoint1_)); |
| EXPECT_TRUE(FindEndpointInCache(source.group1_key, kEndpoint2_)); |
| EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint1_)); |
| EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint2_)); |
| EXPECT_TRUE(EndpointGroupExistsInCache(source.group1_key, |
| OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE(EndpointGroupExistsInCache(source.group2_key, |
| OriginSubdomains::DEFAULT)); |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(static_cast<int>(endpoint_count), |
| mock_store()->StoredEndpointsCount()); |
| EXPECT_EQ(static_cast<int>(endpoint_group_count), |
| mock_store()->StoredEndpointGroupsCount()); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| source.group1_key, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| source.group1_key, kEndpoint2_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| source.group1_key); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| source.group2_key, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| source.group2_key, kEndpoint2_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| source.group2_key); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| } |
| |
| // Check that expected data is present in the ReportingCache at the end. |
| for (const auto& source : kHeaderSources) { |
| EXPECT_TRUE(FindEndpointInCache(source.group1_key, kEndpoint1_)); |
| EXPECT_TRUE(FindEndpointInCache(source.group1_key, kEndpoint2_)); |
| EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint1_)); |
| EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint2_)); |
| EXPECT_TRUE(EndpointGroupExistsInCache(source.group1_key, |
| OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE(EndpointGroupExistsInCache(source.group2_key, |
| OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE(cache()->ClientExistsForTesting( |
| source.network_isolation_key, url::Origin::Create(source.url))); |
| } |
| |
| // Test updating existing configurations |
| |
| // This removes endpoint 1, updates the priority of endpoint 2, and adds |
| // endpoint 3. |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint2_, 2}, |
| {kEndpoint3_}}; |
| // Removes group 1, updates include_subdomains for group 2. |
| std::string header2 = ConstructHeaderGroupString( |
| MakeEndpointGroup(kGroup2_, endpoints2, OriginSubdomains::INCLUDE)); |
| |
| for (const auto& source : kHeaderSources) { |
| // Verify pre-update state |
| EXPECT_TRUE(EndpointGroupExistsInCache(source.group1_key, |
| OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE(EndpointGroupExistsInCache(source.group2_key, |
| OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint1_)); |
| ReportingEndpoint endpoint = |
| FindEndpointInCache(source.group2_key, kEndpoint2_); |
| EXPECT_TRUE(endpoint); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| endpoint.info.priority); |
| EXPECT_FALSE(FindEndpointInCache(source.group2_key, kEndpoint3_)); |
| |
| ParseHeader(source.network_isolation_key, source.url, header2); |
| endpoint_group_count--; |
| endpoint_count -= 2; |
| EXPECT_EQ(endpoint_group_count, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_EQ(endpoint_count, cache()->GetEndpointCount()); |
| |
| // Verify post-update state |
| EXPECT_FALSE(EndpointGroupExistsInCache(source.group1_key, |
| OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE(EndpointGroupExistsInCache(source.group2_key, |
| OriginSubdomains::INCLUDE)); |
| EXPECT_FALSE(FindEndpointInCache(source.group2_key, kEndpoint1_)); |
| endpoint = FindEndpointInCache(source.group2_key, kEndpoint2_); |
| EXPECT_TRUE(endpoint); |
| EXPECT_EQ(2, endpoint.info.priority); |
| EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint3_)); |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(static_cast<int>(endpoint_count), |
| mock_store()->StoredEndpointsCount()); |
| EXPECT_EQ(static_cast<int>(endpoint_group_count), |
| mock_store()->StoredEndpointGroupsCount()); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT, |
| source.group1_key, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT, |
| source.group1_key, kEndpoint2_); |
| expected_commands.emplace_back( |
| CommandType::DELETE_REPORTING_ENDPOINT_GROUP, source.group1_key); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT, |
| source.group2_key, kEndpoint1_); |
| expected_commands.emplace_back( |
| CommandType::UPDATE_REPORTING_ENDPOINT_DETAILS, source.group2_key, |
| kEndpoint2_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| source.group2_key, kEndpoint3_); |
| expected_commands.emplace_back( |
| CommandType::UPDATE_REPORTING_ENDPOINT_GROUP_DETAILS, |
| source.group2_key); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| } |
| |
| // Check that expected data is present in the ReportingCache at the end. |
| for (const auto& source : kHeaderSources) { |
| EXPECT_FALSE(FindEndpointInCache(source.group1_key, kEndpoint1_)); |
| EXPECT_FALSE(FindEndpointInCache(source.group1_key, kEndpoint2_)); |
| EXPECT_FALSE(FindEndpointInCache(source.group2_key, kEndpoint1_)); |
| EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint2_)); |
| EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint3_)); |
| EXPECT_FALSE(EndpointGroupExistsInCache(source.group1_key, |
| OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE(EndpointGroupExistsInCache(source.group2_key, |
| OriginSubdomains::INCLUDE)); |
| EXPECT_TRUE(cache()->ClientExistsForTesting( |
| source.network_isolation_key, url::Origin::Create(source.url))); |
| } |
| } |
| |
| TEST_P(ReportingHeaderParserTest, |
| HeaderErroneouslyContainsMultipleGroupsOfSameName) { |
| // Add a preexisting header to test that a header with multiple groups of the |
| // same name is treated as if it specified a single group with the combined |
| // set of specified endpoints. In particular, it must overwrite/update any |
| // preexisting group all at once. See https://crbug.com/1116529. |
| std::vector<ReportingEndpoint::EndpointInfo> preexisting = {{kEndpoint1_}}; |
| std::string preexisting_header = |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, preexisting)); |
| |
| ParseHeader(kNik_, kUrl1_, preexisting_header); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT)); |
| EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_)); |
| EXPECT_EQ(1u, cache()->GetEndpointCount()); |
| ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey11_, kEndpoint1_); |
| ASSERT_TRUE(endpoint); |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(1, mock_store()->StoredEndpointsCount()); |
| EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount()); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey11_, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey11_); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| // Reset commands so we can check that the next part, adding the header with |
| // duplicate groups, does not cause clearing of preexisting endpoints twice. |
| mock_store()->ClearCommands(); |
| } |
| |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_}}; |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint2_}}; |
| std::string duplicate_groups_header = |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1)) + |
| ", " + |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints2)); |
| |
| ParseHeader(kNik_, kUrl1_, duplicate_groups_header); |
| // Result is as if they set the two groups with the same name as one group. |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT)); |
| EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting()); |
| |
| EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_)); |
| |
| EXPECT_EQ(2u, cache()->GetEndpointCount()); |
| ReportingEndpoint endpoint1 = FindEndpointInCache(kGroupKey11_, kEndpoint1_); |
| ASSERT_TRUE(endpoint); |
| EXPECT_EQ(kOrigin1_, endpoint.group_key.origin); |
| EXPECT_EQ(kGroup1_, endpoint.group_key.group_name); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| endpoint.info.priority); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight, |
| endpoint.info.weight); |
| |
| ReportingEndpoint endpoint2 = FindEndpointInCache(kGroupKey11_, kEndpoint2_); |
| ASSERT_TRUE(endpoint2); |
| EXPECT_EQ(kOrigin1_, endpoint2.group_key.origin); |
| EXPECT_EQ(kGroup1_, endpoint2.group_key.group_name); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| endpoint2.info.priority); |
| EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight, |
| endpoint2.info.weight); |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(2, mock_store()->StoredEndpointsCount()); |
| EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount()); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back( |
| CommandType::UPDATE_REPORTING_ENDPOINT_DETAILS, kGroupKey11_, |
| kEndpoint1_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey11_, kEndpoint2_); |
| expected_commands.emplace_back( |
| CommandType::UPDATE_REPORTING_ENDPOINT_GROUP_DETAILS, kGroupKey11_); |
| MockPersistentReportingStore::CommandList actual_commands = |
| mock_store()->GetAllCommands(); |
| EXPECT_THAT(actual_commands, testing::IsSupersetOf(expected_commands)); |
| for (const auto& command : actual_commands) { |
| EXPECT_NE(CommandType::DELETE_REPORTING_ENDPOINT, command.type); |
| EXPECT_NE(CommandType::DELETE_REPORTING_ENDPOINT_GROUP, command.type); |
| |
| // The endpoint with URL kEndpoint1_ is only ever updated, not added anew. |
| EXPECT_NE( |
| MockPersistentReportingStore::Command( |
| CommandType::ADD_REPORTING_ENDPOINT, kGroupKey11_, kEndpoint1_), |
| command); |
| // The group is only ever updated, not added anew. |
| EXPECT_NE(MockPersistentReportingStore::Command( |
| CommandType::ADD_REPORTING_ENDPOINT_GROUP, kGroupKey11_), |
| command); |
| } |
| } |
| } |
| |
| TEST_P(ReportingHeaderParserTest, |
| HeaderErroneouslyContainsGroupsWithRedundantEndpoints) { |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}, |
| {kEndpoint1_}}; |
| std::string header = |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints)); |
| ParseHeader(kNik_, kUrl1_, header); |
| |
| // We should dedupe the identical endpoint URLs. |
| EXPECT_EQ(1u, cache()->GetEndpointCount()); |
| ASSERT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint1_)); |
| |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT)); |
| EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting()); |
| |
| EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_)); |
| } |
| |
| TEST_P(ReportingHeaderParserTest, |
| HeaderErroneouslyContainsMultipleGroupsOfSameNameAndEndpoints) { |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}}; |
| std::string header = |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints)) + |
| ", " + ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints)); |
| ParseHeader(kNik_, kUrl1_, header); |
| |
| // We should dedupe the identical endpoint URLs, even when they're in |
| // different group. |
| EXPECT_EQ(1u, cache()->GetEndpointCount()); |
| ASSERT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint1_)); |
| |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT)); |
| EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting()); |
| |
| EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_)); |
| } |
| |
| TEST_P(ReportingHeaderParserTest, |
| HeaderErroneouslyContainsGroupsOfSameNameAndOverlappingEndpoints) { |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_}, |
| {kEndpoint2_}}; |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint1_}, |
| {kEndpoint3_}}; |
| std::string header = |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1)) + |
| ", " + |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints2)); |
| ParseHeader(kNik_, kUrl1_, header); |
| |
| // We should dedupe the identical endpoint URLs, even when they're in |
| // different group. |
| EXPECT_EQ(3u, cache()->GetEndpointCount()); |
| ASSERT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint1_)); |
| ASSERT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint2_)); |
| ASSERT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint3_)); |
| |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT)); |
| EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting()); |
| |
| EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_)); |
| } |
| |
| TEST_P(ReportingHeaderParserTest, OverwriteOldHeader) { |
| // First, the origin sets a header with two endpoints in the same group. |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = { |
| {kEndpoint1_, 10 /* priority */}, {kEndpoint2_}}; |
| std::string header1 = |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1)); |
| ParseHeader(kNik_, kUrl1_, header1); |
| |
| EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_)); |
| EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT)); |
| EXPECT_EQ(2u, cache()->GetEndpointCount()); |
| EXPECT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint1_)); |
| EXPECT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint2_)); |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(2, |
| mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT)); |
| EXPECT_EQ(1, mock_store()->CountCommands( |
| CommandType::ADD_REPORTING_ENDPOINT_GROUP)); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey11_, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey11_, kEndpoint2_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey11_); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| |
| // Second header from the same origin should overwrite the previous one. |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = { |
| // This endpoint should update the priority of the existing one. |
| {kEndpoint1_, 20 /* priority */}}; |
| // The second endpoint in this group will be deleted. |
| // This group is new. |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints3 = {{kEndpoint2_}}; |
| std::string header2 = |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints2)) + |
| ", " + |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup2_, endpoints3)); |
| ParseHeader(kNik_, kUrl1_, header2); |
| |
| EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_)); |
| |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey12_, OriginSubdomains::DEFAULT)); |
| |
| EXPECT_EQ(2u, cache()->GetEndpointCount()); |
| EXPECT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint1_)); |
| EXPECT_EQ(20, FindEndpointInCache(kGroupKey11_, kEndpoint1_).info.priority); |
| EXPECT_FALSE(FindEndpointInCache(kGroupKey11_, kEndpoint2_)); |
| EXPECT_TRUE(FindEndpointInCache(kGroupKey12_, kEndpoint2_)); |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(2 + 1, |
| mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT)); |
| EXPECT_EQ(1 + 1, mock_store()->CountCommands( |
| CommandType::ADD_REPORTING_ENDPOINT_GROUP)); |
| EXPECT_EQ( |
| 1, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT)); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey12_, kEndpoint2_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey12_); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT, |
| kGroupKey11_, kEndpoint2_); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| } |
| |
| TEST_P(ReportingHeaderParserTest, OverwriteOldHeaderWithCompletelyNew) { |
| ReportingEndpointGroupKey kGroupKey1(kNik_, kOrigin1_, "1"); |
| ReportingEndpointGroupKey kGroupKey2(kNik_, kOrigin1_, "2"); |
| ReportingEndpointGroupKey kGroupKey3(kNik_, kOrigin1_, "3"); |
| ReportingEndpointGroupKey kGroupKey4(kNik_, kOrigin1_, "4"); |
| ReportingEndpointGroupKey kGroupKey5(kNik_, kOrigin1_, "5"); |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints1_1 = {{MakeURL(10)}, |
| {MakeURL(11)}}; |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints2_1 = {{MakeURL(20)}, |
| {MakeURL(21)}}; |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints3_1 = {{MakeURL(30)}, |
| {MakeURL(31)}}; |
| std::string header1 = |
| ConstructHeaderGroupString(MakeEndpointGroup("1", endpoints1_1)) + ", " + |
| ConstructHeaderGroupString(MakeEndpointGroup("2", endpoints2_1)) + ", " + |
| ConstructHeaderGroupString(MakeEndpointGroup("3", endpoints3_1)); |
| ParseHeader(kNik_, kUrl1_, header1); |
| EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_)); |
| EXPECT_EQ(3u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey1, OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey2, OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey3, OriginSubdomains::DEFAULT)); |
| EXPECT_EQ(6u, cache()->GetEndpointCount()); |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(6, |
| mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT)); |
| EXPECT_EQ(3, mock_store()->CountCommands( |
| CommandType::ADD_REPORTING_ENDPOINT_GROUP)); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey1, endpoints1_1[0].url); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey1, endpoints1_1[1].url); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey2, endpoints2_1[0].url); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey2, endpoints2_1[1].url); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey3, endpoints3_1[0].url); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey3, endpoints3_1[1].url); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey1); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey2); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey3); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| |
| // Replace endpoints in each group with completely new endpoints. |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints1_2 = {{MakeURL(12)}}; |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints2_2 = {{MakeURL(22)}}; |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints3_2 = {{MakeURL(32)}}; |
| std::string header2 = |
| ConstructHeaderGroupString(MakeEndpointGroup("1", endpoints1_2)) + ", " + |
| ConstructHeaderGroupString(MakeEndpointGroup("2", endpoints2_2)) + ", " + |
| ConstructHeaderGroupString(MakeEndpointGroup("3", endpoints3_2)); |
| ParseHeader(kNik_, kUrl1_, header2); |
| EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_)); |
| EXPECT_EQ(3u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey1, OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey2, OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey3, OriginSubdomains::DEFAULT)); |
| EXPECT_EQ(3u, cache()->GetEndpointCount()); |
| EXPECT_TRUE(FindEndpointInCache(kGroupKey1, MakeURL(12))); |
| EXPECT_FALSE(FindEndpointInCache(kGroupKey1, MakeURL(10))); |
| EXPECT_FALSE(FindEndpointInCache(kGroupKey1, MakeURL(11))); |
| EXPECT_TRUE(FindEndpointInCache(kGroupKey2, MakeURL(22))); |
| EXPECT_FALSE(FindEndpointInCache(kGroupKey2, MakeURL(20))); |
| EXPECT_FALSE(FindEndpointInCache(kGroupKey2, MakeURL(21))); |
| EXPECT_TRUE(FindEndpointInCache(kGroupKey3, MakeURL(32))); |
| EXPECT_FALSE(FindEndpointInCache(kGroupKey3, MakeURL(30))); |
| EXPECT_FALSE(FindEndpointInCache(kGroupKey3, MakeURL(31))); |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(6 + 3, |
| mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT)); |
| EXPECT_EQ(3, mock_store()->CountCommands( |
| CommandType::ADD_REPORTING_ENDPOINT_GROUP)); |
| EXPECT_EQ( |
| 6, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT)); |
| EXPECT_EQ(0, mock_store()->CountCommands( |
| CommandType::DELETE_REPORTING_ENDPOINT_GROUP)); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey1, endpoints1_2[0].url); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey2, endpoints2_2[0].url); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey3, endpoints3_2[0].url); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT, |
| kGroupKey1, endpoints1_1[0].url); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT, |
| kGroupKey1, endpoints1_1[1].url); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT, |
| kGroupKey2, endpoints2_1[0].url); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT, |
| kGroupKey2, endpoints2_1[1].url); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT, |
| kGroupKey3, endpoints3_1[0].url); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT, |
| kGroupKey3, endpoints3_1[1].url); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| |
| // Replace all the groups with completely new groups. |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints4_3 = {{MakeURL(40)}}; |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints5_3 = {{MakeURL(50)}}; |
| std::string header3 = |
| ConstructHeaderGroupString(MakeEndpointGroup("4", endpoints4_3)) + ", " + |
| ConstructHeaderGroupString(MakeEndpointGroup("5", endpoints5_3)); |
| ParseHeader(kNik_, kUrl1_, header3); |
| EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_)); |
| EXPECT_EQ(2u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey4, OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey4, OriginSubdomains::DEFAULT)); |
| EXPECT_FALSE( |
| EndpointGroupExistsInCache(kGroupKey1, OriginSubdomains::DEFAULT)); |
| EXPECT_FALSE( |
| EndpointGroupExistsInCache(kGroupKey2, OriginSubdomains::DEFAULT)); |
| EXPECT_FALSE( |
| EndpointGroupExistsInCache(kGroupKey3, OriginSubdomains::DEFAULT)); |
| EXPECT_EQ(2u, cache()->GetEndpointCount()); |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(6 + 3 + 2, |
| mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT)); |
| EXPECT_EQ(3 + 2, mock_store()->CountCommands( |
| CommandType::ADD_REPORTING_ENDPOINT_GROUP)); |
| EXPECT_EQ(6 + 3, mock_store()->CountCommands( |
| CommandType::DELETE_REPORTING_ENDPOINT)); |
| EXPECT_EQ(3, mock_store()->CountCommands( |
| CommandType::DELETE_REPORTING_ENDPOINT_GROUP)); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey4, endpoints4_3[0].url); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey5, endpoints5_3[0].url); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey4); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey5); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT, |
| kGroupKey1, endpoints1_2[0].url); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT, |
| kGroupKey2, endpoints2_2[0].url); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT, |
| kGroupKey3, endpoints3_2[0].url); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP, |
| kGroupKey1); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP, |
| kGroupKey2); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP, |
| kGroupKey3); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| } |
| |
| TEST_P(ReportingHeaderParserTest, ZeroMaxAgeRemovesEndpointGroup) { |
| // Without a pre-existing client, max_age: 0 should do nothing. |
| ASSERT_EQ(0u, cache()->GetEndpointCount()); |
| ParseHeader(kNik_, kUrl1_, |
| "{\"endpoints\":[{\"url\":\"" + kEndpoint1_.spec() + |
| "\"}],\"max_age\":0}"); |
| EXPECT_EQ(0u, cache()->GetEndpointCount()); |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(0, |
| mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT)); |
| EXPECT_EQ(0, mock_store()->CountCommands( |
| CommandType::ADD_REPORTING_ENDPOINT_GROUP)); |
| } |
| |
| // Set a header with two endpoint groups. |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_}}; |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint2_}}; |
| std::string header1 = |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1)) + |
| ", " + |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup2_, endpoints2)); |
| ParseHeader(kNik_, kUrl1_, header1); |
| |
| EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_)); |
| EXPECT_EQ(2u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT)); |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey12_, OriginSubdomains::DEFAULT)); |
| EXPECT_EQ(2u, cache()->GetEndpointCount()); |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(2, |
| mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT)); |
| EXPECT_EQ(2, mock_store()->CountCommands( |
| CommandType::ADD_REPORTING_ENDPOINT_GROUP)); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey11_, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey12_, kEndpoint2_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey11_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey12_); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| |
| // Set another header with max_age: 0 to delete one of the groups. |
| std::string header2 = ConstructHeaderGroupString(MakeEndpointGroup( |
| kGroup1_, endpoints1, OriginSubdomains::DEFAULT, |
| base::TimeDelta::FromSeconds(0))) + |
| ", " + |
| ConstructHeaderGroupString(MakeEndpointGroup( |
| kGroup2_, endpoints2)); // Other group stays. |
| ParseHeader(kNik_, kUrl1_, header2); |
| |
| EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_)); |
| EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting()); |
| |
| // Group was deleted. |
| EXPECT_FALSE( |
| EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT)); |
| // Other group remains in the cache. |
| EXPECT_TRUE( |
| EndpointGroupExistsInCache(kGroupKey12_, OriginSubdomains::DEFAULT)); |
| EXPECT_EQ(1u, cache()->GetEndpointCount()); |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(2, |
| mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT)); |
| EXPECT_EQ(2, mock_store()->CountCommands( |
| CommandType::ADD_REPORTING_ENDPOINT_GROUP)); |
| EXPECT_EQ( |
| 1, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT)); |
| EXPECT_EQ(1, mock_store()->CountCommands( |
| CommandType::DELETE_REPORTING_ENDPOINT_GROUP)); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT, |
| kGroupKey11_, kEndpoint1_); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP, |
| kGroupKey11_); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| |
| // Set another header with max_age: 0 to delete the other group. (Should work |
| // even if the endpoints field is an empty list.) |
| std::string header3 = ConstructHeaderGroupString(MakeEndpointGroup( |
| kGroup2_, std::vector<ReportingEndpoint::EndpointInfo>(), |
| OriginSubdomains::DEFAULT, base::TimeDelta::FromSeconds(0))); |
| ParseHeader(kNik_, kUrl1_, header3); |
| |
| // Deletion of the last remaining group also deletes the client for this |
| // origin. |
| EXPECT_FALSE(ClientExistsInCacheForOrigin(kOrigin1_)); |
| EXPECT_EQ(0u, cache()->GetEndpointGroupCountForTesting()); |
| EXPECT_EQ(0u, cache()->GetEndpointCount()); |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(2, |
| mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT)); |
| EXPECT_EQ(2, mock_store()->CountCommands( |
| CommandType::ADD_REPORTING_ENDPOINT_GROUP)); |
| EXPECT_EQ(1 + 1, mock_store()->CountCommands( |
| CommandType::DELETE_REPORTING_ENDPOINT)); |
| EXPECT_EQ(1 + 1, mock_store()->CountCommands( |
| CommandType::DELETE_REPORTING_ENDPOINT_GROUP)); |
| MockPersistentReportingStore::CommandList expected_commands; |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT, |
| kGroupKey12_, kEndpoint2_); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP, |
| kGroupKey12_); |
| EXPECT_THAT(mock_store()->GetAllCommands(), |
| testing::IsSupersetOf(expected_commands)); |
| } |
| } |
| |
| TEST_P(ReportingHeaderParserTest, EvictEndpointsOverPerOriginLimit1) { |
| // Set a header with too many endpoints, all in the same group. |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints; |
| for (size_t i = 0; i < policy().max_endpoints_per_origin + 1; ++i) { |
| endpoints.push_back({MakeURL(i)}); |
| } |
| std::string header = |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints)); |
| ParseHeader(kNik_, kUrl1_, header); |
| |
| // Endpoint count should be at most the limit. |
| EXPECT_GE(policy().max_endpoints_per_origin, cache()->GetEndpointCount()); |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(policy().max_endpoints_per_origin + 1, |
| static_cast<unsigned long>(mock_store()->CountCommands( |
| CommandType::ADD_REPORTING_ENDPOINT))); |
| EXPECT_EQ(1, mock_store()->CountCommands( |
| CommandType::ADD_REPORTING_ENDPOINT_GROUP)); |
| EXPECT_EQ( |
| 1, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT)); |
| } |
| } |
| |
| TEST_P(ReportingHeaderParserTest, EvictEndpointsOverPerOriginLimit2) { |
| // Set a header with too many endpoints, in different groups. |
| std::string header; |
| for (size_t i = 0; i < policy().max_endpoints_per_origin + 1; ++i) { |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{MakeURL(i)}}; |
| header = header + ConstructHeaderGroupString(MakeEndpointGroup( |
| base::NumberToString(i), endpoints)); |
| if (i != policy().max_endpoints_per_origin) |
| header = header + ", "; |
| } |
| ParseHeader(kNik_, kUrl1_, header); |
| |
| // Endpoint count should be at most the limit. |
| EXPECT_GE(policy().max_endpoints_per_origin, cache()->GetEndpointCount()); |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(policy().max_endpoints_per_origin + 1, |
| static_cast<unsigned long>(mock_store()->CountCommands( |
| CommandType::ADD_REPORTING_ENDPOINT))); |
| EXPECT_EQ(policy().max_endpoints_per_origin + 1, |
| static_cast<unsigned long>(mock_store()->CountCommands( |
| CommandType::ADD_REPORTING_ENDPOINT_GROUP))); |
| EXPECT_EQ( |
| 1, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT)); |
| EXPECT_EQ(1, mock_store()->CountCommands( |
| CommandType::DELETE_REPORTING_ENDPOINT_GROUP)); |
| } |
| } |
| |
| TEST_P(ReportingHeaderParserTest, EvictEndpointsOverGlobalLimit) { |
| // Set headers from different origins up to the global limit. |
| for (size_t i = 0; i < policy().max_endpoint_count; ++i) { |
| std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{MakeURL(i)}}; |
| std::string header = |
| ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints)); |
| ParseHeader(kNik_, MakeURL(i), header); |
| } |
| EXPECT_EQ(policy().max_endpoint_count, cache()->GetEndpointCount()); |
| |
| // Parse one more header to trigger eviction. |
| ParseHeader(kNik_, kUrl1_, |
| "{\"endpoints\":[{\"url\":\"" + kEndpoint1_.spec() + |
| "\"}],\"max_age\":1}"); |
| |
| // Endpoint count should be at most the limit. |
| EXPECT_GE(policy().max_endpoint_count, cache()->GetEndpointCount()); |
| |
| if (mock_store()) { |
| mock_store()->Flush(); |
| EXPECT_EQ(policy().max_endpoint_count + 1, |
| static_cast<unsigned long>(mock_store()->CountCommands( |
| CommandType::ADD_REPORTING_ENDPOINT))); |
| EXPECT_EQ(policy().max_endpoint_count + 1, |
| static_cast<unsigned long>(mock_store()->CountCommands( |
| CommandType::ADD_REPORTING_ENDPOINT_GROUP))); |
| EXPECT_EQ( |
| 1, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT)); |
| EXPECT_EQ(1, mock_store()->CountCommands( |
| CommandType::DELETE_REPORTING_ENDPOINT_GROUP)); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(ReportingHeaderParserStoreTest, |
| ReportingHeaderParserTest, |
| testing::Bool()); |
| |
| } // namespace |
| } // namespace net |