| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/reporting/reporting_endpoint_manager.h" |
| |
| #include <string> |
| |
| #include "base/strings/stringprintf.h" |
| #include "base/test/simple_test_tick_clock.h" |
| #include "base/time/time.h" |
| #include "base/unguessable_token.h" |
| #include "net/base/backoff_entry.h" |
| #include "net/base/isolation_info.h" |
| #include "net/base/network_anonymization_key.h" |
| #include "net/base/schemeful_site.h" |
| #include "net/reporting/reporting_cache.h" |
| #include "net/reporting/reporting_endpoint.h" |
| #include "net/reporting/reporting_policy.h" |
| #include "net/reporting/reporting_test_util.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| |
| namespace net { |
| namespace { |
| |
| class TestReportingCache : public ReportingCache { |
| public: |
| class PersistentReportingStore; |
| |
| // Tests using this class only use one origin/group. |
| TestReportingCache(const url::Origin& expected_origin, |
| const std::string& expected_group) |
| : expected_origin_(expected_origin), expected_group_(expected_group) {} |
| |
| TestReportingCache(const TestReportingCache&) = delete; |
| TestReportingCache& operator=(const TestReportingCache&) = delete; |
| |
| ~TestReportingCache() override = default; |
| |
| void SetEndpoint(const ReportingEndpoint& reporting_endpoint) { |
| reporting_endpoints_[reporting_endpoint.group_key.network_anonymization_key] |
| .push_back(reporting_endpoint); |
| } |
| |
| // ReportingCache implementation: |
| |
| std::vector<ReportingEndpoint> GetCandidateEndpointsForDelivery( |
| const ReportingEndpointGroupKey& group_key) override { |
| EXPECT_EQ(expected_origin_, group_key.origin); |
| EXPECT_EQ(expected_group_, group_key.group_name); |
| return reporting_endpoints_[group_key.network_anonymization_key]; |
| } |
| |
| // Everything below is NOTREACHED. |
| void AddReport(const absl::optional<base::UnguessableToken>& reporting_source, |
| const NetworkAnonymizationKey& network_anonymization_key, |
| const GURL& url, |
| const std::string& user_agent, |
| const std::string& group_name, |
| const std::string& type, |
| base::Value::Dict body, |
| int depth, |
| base::TimeTicks queued, |
| int attempts) override { |
| NOTREACHED(); |
| } |
| void GetReports( |
| std::vector<const ReportingReport*>* reports_out) const override { |
| NOTREACHED(); |
| } |
| base::Value GetReportsAsValue() const override { |
| NOTREACHED(); |
| return base::Value(); |
| } |
| std::vector<const ReportingReport*> GetReportsToDeliver() override { |
| NOTREACHED(); |
| return {}; |
| } |
| std::vector<const ReportingReport*> GetReportsToDeliverForSource( |
| const base::UnguessableToken& reporting_source) override { |
| NOTREACHED(); |
| return {}; |
| } |
| void ClearReportsPending( |
| const std::vector<const ReportingReport*>& reports) override { |
| NOTREACHED(); |
| } |
| void IncrementReportsAttempts( |
| const std::vector<const ReportingReport*>& reports) override { |
| NOTREACHED(); |
| } |
| base::flat_map<url::Origin, std::vector<ReportingEndpoint>> |
| GetV1ReportingEndpointsByOrigin() const override { |
| NOTREACHED(); |
| return base::flat_map<url::Origin, std::vector<ReportingEndpoint>>(); |
| } |
| void IncrementEndpointDeliveries(const ReportingEndpointGroupKey& group_key, |
| const GURL& url, |
| int reports_delivered, |
| bool successful) override { |
| NOTREACHED(); |
| } |
| void SetExpiredSource( |
| const base::UnguessableToken& reporting_source) override { |
| NOTREACHED(); |
| } |
| const base::flat_set<base::UnguessableToken>& GetExpiredSources() |
| const override { |
| NOTREACHED(); |
| return expired_sources_; |
| } |
| void RemoveReports( |
| const std::vector<const ReportingReport*>& reports) override { |
| NOTREACHED(); |
| } |
| void RemoveReports(const std::vector<const ReportingReport*>& reports, |
| bool delivery_success) override { |
| NOTREACHED(); |
| } |
| void RemoveAllReports() override { NOTREACHED(); } |
| size_t GetFullReportCountForTesting() const override { |
| NOTREACHED(); |
| return 0; |
| } |
| size_t GetReportCountWithStatusForTesting( |
| ReportingReport::Status status) const override { |
| NOTREACHED(); |
| return 0; |
| } |
| bool IsReportPendingForTesting(const ReportingReport* report) const override { |
| NOTREACHED(); |
| return false; |
| } |
| bool IsReportDoomedForTesting(const ReportingReport* report) const override { |
| NOTREACHED(); |
| return false; |
| } |
| void OnParsedHeader( |
| const NetworkAnonymizationKey& network_anonymization_key, |
| const url::Origin& origin, |
| std::vector<ReportingEndpointGroup> parsed_header) override { |
| NOTREACHED(); |
| } |
| void OnParsedReportingEndpointsHeader( |
| const base::UnguessableToken& reporting_source, |
| const IsolationInfo& isolation_info, |
| std::vector<ReportingEndpoint> endpoints) override { |
| NOTREACHED(); |
| } |
| std::set<url::Origin> GetAllOrigins() const override { |
| NOTREACHED(); |
| return std::set<url::Origin>(); |
| } |
| void RemoveClient(const NetworkAnonymizationKey& network_anonymization_key, |
| const url::Origin& origin) override { |
| NOTREACHED(); |
| } |
| void RemoveClientsForOrigin(const url::Origin& origin) override { |
| NOTREACHED(); |
| } |
| void RemoveAllClients() override { NOTREACHED(); } |
| void RemoveEndpointGroup( |
| const ReportingEndpointGroupKey& group_key) override { |
| NOTREACHED(); |
| } |
| void RemoveEndpointsForUrl(const GURL& url) override { NOTREACHED(); } |
| void RemoveSourceAndEndpoints( |
| const base::UnguessableToken& reporting_source) override { |
| NOTREACHED(); |
| } |
| void AddClientsLoadedFromStore( |
| std::vector<ReportingEndpoint> loaded_endpoints, |
| std::vector<CachedReportingEndpointGroup> loaded_endpoint_groups) |
| override { |
| NOTREACHED(); |
| } |
| base::Value GetClientsAsValue() const override { |
| NOTREACHED(); |
| return base::Value(); |
| } |
| size_t GetEndpointCount() const override { |
| NOTREACHED(); |
| return 0; |
| } |
| void Flush() override { NOTREACHED(); } |
| ReportingEndpoint GetV1EndpointForTesting( |
| const base::UnguessableToken& reporting_source, |
| const std::string& endpoint_name) const override { |
| NOTREACHED(); |
| return ReportingEndpoint(); |
| } |
| ReportingEndpoint GetEndpointForTesting( |
| const ReportingEndpointGroupKey& group_key, |
| const GURL& url) const override { |
| NOTREACHED(); |
| return ReportingEndpoint(); |
| } |
| bool EndpointGroupExistsForTesting(const ReportingEndpointGroupKey& group_key, |
| OriginSubdomains include_subdomains, |
| base::Time expires) const override { |
| NOTREACHED(); |
| return false; |
| } |
| bool ClientExistsForTesting( |
| const NetworkAnonymizationKey& network_anonymization_key, |
| const url::Origin& origin) const override { |
| NOTREACHED(); |
| return false; |
| } |
| size_t GetEndpointGroupCountForTesting() const override { |
| NOTREACHED(); |
| return 0; |
| } |
| size_t GetClientCountForTesting() const override { |
| NOTREACHED(); |
| return 0; |
| } |
| size_t GetReportingSourceCountForTesting() const override { |
| NOTREACHED(); |
| return 0; |
| } |
| void SetEndpointForTesting(const ReportingEndpointGroupKey& group_key, |
| const GURL& url, |
| OriginSubdomains include_subdomains, |
| base::Time expires, |
| int priority, |
| int weight) override { |
| NOTREACHED(); |
| } |
| void SetV1EndpointForTesting(const ReportingEndpointGroupKey& group_key, |
| const base::UnguessableToken& reporting_source, |
| const IsolationInfo& isolation_info, |
| const GURL& url) override { |
| NOTREACHED(); |
| } |
| IsolationInfo GetIsolationInfoForEndpoint( |
| const ReportingEndpoint& endpoint) const override { |
| NOTREACHED(); |
| return IsolationInfo(); |
| } |
| |
| private: |
| const url::Origin expected_origin_; |
| const std::string expected_group_; |
| |
| std::map<NetworkAnonymizationKey, std::vector<ReportingEndpoint>> |
| reporting_endpoints_; |
| base::flat_set<base::UnguessableToken> expired_sources_; |
| }; |
| |
| class ReportingEndpointManagerTest : public testing::Test { |
| public: |
| ReportingEndpointManagerTest() : cache_(kOrigin, kGroup) { |
| policy_.endpoint_backoff_policy.num_errors_to_ignore = 0; |
| policy_.endpoint_backoff_policy.initial_delay_ms = 60000; |
| policy_.endpoint_backoff_policy.multiply_factor = 2.0; |
| policy_.endpoint_backoff_policy.jitter_factor = 0.0; |
| policy_.endpoint_backoff_policy.maximum_backoff_ms = -1; |
| policy_.endpoint_backoff_policy.entry_lifetime_ms = 0; |
| policy_.endpoint_backoff_policy.always_use_initial_delay = false; |
| |
| clock_.SetNowTicks(base::TimeTicks()); |
| |
| endpoint_manager_ = ReportingEndpointManager::Create( |
| &policy_, &clock_, &delegate_, &cache_, TestReportingRandIntCallback()); |
| } |
| |
| protected: |
| void SetEndpoint( |
| const GURL& endpoint, |
| int priority = ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| int weight = ReportingEndpoint::EndpointInfo::kDefaultWeight, |
| const NetworkAnonymizationKey& network_anonymization_key = |
| NetworkAnonymizationKey()) { |
| ReportingEndpointGroupKey group_key(kGroupKey); |
| group_key.network_anonymization_key = network_anonymization_key; |
| cache_.SetEndpoint(ReportingEndpoint( |
| group_key, |
| ReportingEndpoint::EndpointInfo{endpoint, priority, weight})); |
| } |
| |
| const NetworkAnonymizationKey kNik; |
| const url::Origin kOrigin = url::Origin::Create(GURL("https://origin/")); |
| const SchemefulSite kSite = SchemefulSite(kOrigin); |
| const std::string kGroup = "group"; |
| const ReportingEndpointGroupKey kGroupKey = |
| ReportingEndpointGroupKey(kNik, kOrigin, kGroup); |
| const GURL kEndpoint = GURL("https://endpoint/"); |
| |
| ReportingPolicy policy_; |
| base::SimpleTestTickClock clock_; |
| TestReportingDelegate delegate_; |
| TestReportingCache cache_; |
| std::unique_ptr<ReportingEndpointManager> endpoint_manager_; |
| }; |
| |
| TEST_F(ReportingEndpointManagerTest, NoEndpoint) { |
| ReportingEndpoint endpoint = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey); |
| EXPECT_FALSE(endpoint); |
| } |
| |
| TEST_F(ReportingEndpointManagerTest, Endpoint) { |
| SetEndpoint(kEndpoint); |
| |
| ReportingEndpoint endpoint = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey); |
| ASSERT_TRUE(endpoint); |
| EXPECT_EQ(kEndpoint, endpoint.info.url); |
| } |
| |
| TEST_F(ReportingEndpointManagerTest, BackedOffEndpoint) { |
| ASSERT_EQ(2.0, policy_.endpoint_backoff_policy.multiply_factor); |
| |
| base::TimeDelta initial_delay = |
| base::Milliseconds(policy_.endpoint_backoff_policy.initial_delay_ms); |
| |
| SetEndpoint(kEndpoint); |
| |
| endpoint_manager_->InformOfEndpointRequest(NetworkAnonymizationKey(), |
| kEndpoint, false); |
| |
| // After one failure, endpoint is in exponential backoff. |
| ReportingEndpoint endpoint = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey); |
| EXPECT_FALSE(endpoint); |
| |
| // After initial delay, endpoint is usable again. |
| clock_.Advance(initial_delay); |
| |
| ReportingEndpoint endpoint2 = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey); |
| ASSERT_TRUE(endpoint2); |
| EXPECT_EQ(kEndpoint, endpoint2.info.url); |
| |
| endpoint_manager_->InformOfEndpointRequest(NetworkAnonymizationKey(), |
| kEndpoint, false); |
| |
| // After a second failure, endpoint is backed off again. |
| ReportingEndpoint endpoint3 = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey); |
| EXPECT_FALSE(endpoint3); |
| |
| clock_.Advance(initial_delay); |
| |
| // Next backoff is longer -- 2x the first -- so endpoint isn't usable yet. |
| ReportingEndpoint endpoint4 = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey); |
| EXPECT_FALSE(endpoint4); |
| |
| clock_.Advance(initial_delay); |
| |
| // After 2x the initial delay, the endpoint is usable again. |
| ReportingEndpoint endpoint5 = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey); |
| ASSERT_TRUE(endpoint5); |
| EXPECT_EQ(kEndpoint, endpoint5.info.url); |
| |
| endpoint_manager_->InformOfEndpointRequest(NetworkAnonymizationKey(), |
| kEndpoint, true); |
| endpoint_manager_->InformOfEndpointRequest(NetworkAnonymizationKey(), |
| kEndpoint, true); |
| |
| // Two more successful requests should reset the backoff to the initial delay |
| // again. |
| endpoint_manager_->InformOfEndpointRequest(NetworkAnonymizationKey(), |
| kEndpoint, false); |
| |
| ReportingEndpoint endpoint6 = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey); |
| EXPECT_FALSE(endpoint6); |
| |
| clock_.Advance(initial_delay); |
| |
| ReportingEndpoint endpoint7 = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey); |
| EXPECT_TRUE(endpoint7); |
| } |
| |
| // Make sure that multiple endpoints will all be returned at some point, to |
| // avoid accidentally or intentionally implementing any priority ordering. |
| TEST_F(ReportingEndpointManagerTest, RandomEndpoint) { |
| static const GURL kEndpoint1("https://endpoint1/"); |
| static const GURL kEndpoint2("https://endpoint2/"); |
| static const int kMaxAttempts = 20; |
| |
| SetEndpoint(kEndpoint1); |
| SetEndpoint(kEndpoint2); |
| |
| bool endpoint1_seen = false; |
| bool endpoint2_seen = false; |
| |
| for (int i = 0; i < kMaxAttempts; ++i) { |
| ReportingEndpoint endpoint = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey); |
| ASSERT_TRUE(endpoint); |
| ASSERT_TRUE(endpoint.info.url == kEndpoint1 || |
| endpoint.info.url == kEndpoint2); |
| |
| if (endpoint.info.url == kEndpoint1) |
| endpoint1_seen = true; |
| else if (endpoint.info.url == kEndpoint2) |
| endpoint2_seen = true; |
| |
| if (endpoint1_seen && endpoint2_seen) |
| break; |
| } |
| |
| EXPECT_TRUE(endpoint1_seen); |
| EXPECT_TRUE(endpoint2_seen); |
| } |
| |
| TEST_F(ReportingEndpointManagerTest, Priority) { |
| static const GURL kPrimaryEndpoint("https://endpoint1/"); |
| static const GURL kBackupEndpoint("https://endpoint2/"); |
| |
| SetEndpoint(kPrimaryEndpoint, 10 /* priority */, |
| ReportingEndpoint::EndpointInfo::kDefaultWeight); |
| SetEndpoint(kBackupEndpoint, 20 /* priority */, |
| ReportingEndpoint::EndpointInfo::kDefaultWeight); |
| |
| ReportingEndpoint endpoint = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey); |
| ASSERT_TRUE(endpoint); |
| EXPECT_EQ(kPrimaryEndpoint, endpoint.info.url); |
| |
| // The backoff policy we set up in the constructor means that a single failed |
| // upload will take the primary endpoint out of contention. This should cause |
| // us to choose the backend endpoint. |
| endpoint_manager_->InformOfEndpointRequest(NetworkAnonymizationKey(), |
| kPrimaryEndpoint, false); |
| ReportingEndpoint endpoint2 = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey); |
| ASSERT_TRUE(endpoint2); |
| EXPECT_EQ(kBackupEndpoint, endpoint2.info.url); |
| |
| // Advance the current time far enough to clear out the primary endpoint's |
| // backoff clock. This should bring the primary endpoint back into play. |
| clock_.Advance(base::Minutes(2)); |
| ReportingEndpoint endpoint3 = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey); |
| ASSERT_TRUE(endpoint3); |
| EXPECT_EQ(kPrimaryEndpoint, endpoint3.info.url); |
| } |
| |
| // Note: This test depends on the deterministic mock RandIntCallback set up in |
| // TestReportingContext, which returns consecutive integers starting at 0 |
| // (modulo the requested range, plus the requested minimum). |
| TEST_F(ReportingEndpointManagerTest, Weight) { |
| static const GURL kEndpoint1("https://endpoint1/"); |
| static const GURL kEndpoint2("https://endpoint2/"); |
| |
| static const int kEndpoint1Weight = 5; |
| static const int kEndpoint2Weight = 2; |
| static const int kTotalEndpointWeight = kEndpoint1Weight + kEndpoint2Weight; |
| |
| SetEndpoint(kEndpoint1, ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| kEndpoint1Weight); |
| SetEndpoint(kEndpoint2, ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| kEndpoint2Weight); |
| |
| int endpoint1_count = 0; |
| int endpoint2_count = 0; |
| |
| for (int i = 0; i < kTotalEndpointWeight; ++i) { |
| ReportingEndpoint endpoint = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey); |
| ASSERT_TRUE(endpoint); |
| ASSERT_TRUE(endpoint.info.url == kEndpoint1 || |
| endpoint.info.url == kEndpoint2); |
| |
| if (endpoint.info.url == kEndpoint1) |
| ++endpoint1_count; |
| else if (endpoint.info.url == kEndpoint2) |
| ++endpoint2_count; |
| } |
| |
| EXPECT_EQ(kEndpoint1Weight, endpoint1_count); |
| EXPECT_EQ(kEndpoint2Weight, endpoint2_count); |
| } |
| |
| TEST_F(ReportingEndpointManagerTest, ZeroWeights) { |
| static const GURL kEndpoint1("https://endpoint1/"); |
| static const GURL kEndpoint2("https://endpoint2/"); |
| |
| SetEndpoint(kEndpoint1, ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| 0 /* weight */); |
| SetEndpoint(kEndpoint2, ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| 0 /* weight */); |
| |
| int endpoint1_count = 0; |
| int endpoint2_count = 0; |
| |
| for (int i = 0; i < 10; ++i) { |
| ReportingEndpoint endpoint = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey); |
| ASSERT_TRUE(endpoint); |
| ASSERT_TRUE(endpoint.info.url == kEndpoint1 || |
| endpoint.info.url == kEndpoint2); |
| |
| if (endpoint.info.url == kEndpoint1) |
| ++endpoint1_count; |
| else if (endpoint.info.url == kEndpoint2) |
| ++endpoint2_count; |
| } |
| |
| EXPECT_EQ(5, endpoint1_count); |
| EXPECT_EQ(5, endpoint2_count); |
| } |
| |
| // Check that ReportingEndpointManager distinguishes NetworkAnonymizationKeys. |
| TEST_F(ReportingEndpointManagerTest, NetworkAnonymizationKey) { |
| const SchemefulSite kSite2(GURL("https://origin2/")); |
| |
| const auto kNetworkAnonymizationKey1 = |
| NetworkAnonymizationKey::CreateSameSite(kSite); |
| const auto kNetworkAnonymizationKey2 = |
| NetworkAnonymizationKey::CreateSameSite(kSite2); |
| const ReportingEndpointGroupKey kGroupKey1(kNetworkAnonymizationKey1, kOrigin, |
| kGroup); |
| const ReportingEndpointGroupKey kGroupKey2(kNetworkAnonymizationKey2, kOrigin, |
| kGroup); |
| |
| // An Endpoint set for kNetworkAnonymizationKey1 should not affect |
| // kNetworkAnonymizationKey2. |
| SetEndpoint(kEndpoint, ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| 0 /* weight */, kNetworkAnonymizationKey1); |
| ReportingEndpoint endpoint = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey1); |
| ASSERT_TRUE(endpoint); |
| EXPECT_EQ(kEndpoint, endpoint.info.url); |
| EXPECT_FALSE(endpoint_manager_->FindEndpointForDelivery(kGroupKey2)); |
| EXPECT_FALSE(endpoint_manager_->FindEndpointForDelivery(kGroupKey)); |
| |
| // Set the same Endpoint for kNetworkAnonymizationKey2, so both should be |
| // reporting to the same URL. |
| SetEndpoint(kEndpoint, ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| 0 /* weight */, kNetworkAnonymizationKey2); |
| endpoint = endpoint_manager_->FindEndpointForDelivery(kGroupKey1); |
| ASSERT_TRUE(endpoint); |
| EXPECT_EQ(kEndpoint, endpoint.info.url); |
| endpoint = endpoint_manager_->FindEndpointForDelivery(kGroupKey2); |
| ASSERT_TRUE(endpoint); |
| EXPECT_EQ(kEndpoint, endpoint.info.url); |
| EXPECT_FALSE(endpoint_manager_->FindEndpointForDelivery(kGroupKey)); |
| |
| // An error reporting to that URL in the context of kNetworkAnonymizationKey1 |
| // should only affect the Endpoint retrieved in the context of |
| // kNetworkAnonymizationKey1. |
| endpoint_manager_->InformOfEndpointRequest(kNetworkAnonymizationKey1, |
| kEndpoint, false); |
| EXPECT_FALSE(endpoint_manager_->FindEndpointForDelivery(kGroupKey1)); |
| endpoint = endpoint_manager_->FindEndpointForDelivery(kGroupKey2); |
| ASSERT_TRUE(endpoint); |
| EXPECT_EQ(kEndpoint, endpoint.info.url); |
| EXPECT_FALSE(endpoint_manager_->FindEndpointForDelivery(kGroupKey)); |
| } |
| |
| TEST_F(ReportingEndpointManagerTest, |
| NetworkAnonymizationKeyWithMultipleEndpoints) { |
| const SchemefulSite kSite2(GURL("https://origin2/")); |
| |
| const auto kNetworkAnonymizationKey1 = |
| NetworkAnonymizationKey::CreateSameSite(kSite); |
| const auto kNetworkAnonymizationKey2 = |
| NetworkAnonymizationKey::CreateSameSite(kSite2); |
| const ReportingEndpointGroupKey kGroupKey1(kNetworkAnonymizationKey1, kOrigin, |
| kGroup); |
| const ReportingEndpointGroupKey kGroupKey2(kNetworkAnonymizationKey2, kOrigin, |
| kGroup); |
| |
| const GURL kEndpoint1("https://endpoint1/"); |
| const GURL kEndpoint2("https://endpoint2/"); |
| const GURL kEndpoint3("https://endpoint3/"); |
| const int kMaxAttempts = 20; |
| |
| // Add two Endpoints for kNetworkAnonymizationKey1, and a different one for |
| // kNetworkAnonymizationKey2. |
| SetEndpoint(kEndpoint1, ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| ReportingEndpoint::EndpointInfo::kDefaultWeight, |
| kNetworkAnonymizationKey1); |
| SetEndpoint(kEndpoint2, ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| ReportingEndpoint::EndpointInfo::kDefaultWeight, |
| kNetworkAnonymizationKey1); |
| SetEndpoint(kEndpoint3, ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| ReportingEndpoint::EndpointInfo::kDefaultWeight, |
| kNetworkAnonymizationKey2); |
| |
| bool endpoint1_seen = false; |
| bool endpoint2_seen = false; |
| |
| // Make sure that calling FindEndpointForDelivery() with |
| // kNetworkAnonymizationKey1 can return both of its endpoints, but not |
| // kNetworkAnonymizationKey2's endpoint. |
| for (int i = 0; i < kMaxAttempts; ++i) { |
| ReportingEndpoint endpoint = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey1); |
| ASSERT_TRUE(endpoint); |
| ASSERT_TRUE(endpoint.info.url == kEndpoint1 || |
| endpoint.info.url == kEndpoint2); |
| |
| if (endpoint.info.url == kEndpoint1) { |
| endpoint1_seen = true; |
| } else if (endpoint.info.url == kEndpoint2) { |
| endpoint2_seen = true; |
| } |
| } |
| |
| EXPECT_TRUE(endpoint1_seen); |
| EXPECT_TRUE(endpoint2_seen); |
| |
| ReportingEndpoint endpoint = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey2); |
| ASSERT_TRUE(endpoint); |
| EXPECT_EQ(kEndpoint3, endpoint.info.url); |
| } |
| |
| TEST_F(ReportingEndpointManagerTest, CacheEviction) { |
| // Add |kMaxEndpointBackoffCacheSize| endpoints. |
| for (int i = 0; i < ReportingEndpointManager::kMaxEndpointBackoffCacheSize; |
| ++i) { |
| SetEndpoint(GURL(base::StringPrintf("https://endpoint%i/", i))); |
| } |
| |
| // Mark each endpoint as bad, one-at-a-time. Use FindEndpointForDelivery() to |
| // pick which one to mark as bad, both to exercise the code walking through |
| // all endpoints, and as a consistency check. |
| std::set<GURL> seen_endpoints; |
| for (int i = 0; i < ReportingEndpointManager::kMaxEndpointBackoffCacheSize; |
| ++i) { |
| ReportingEndpoint endpoint = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey); |
| EXPECT_TRUE(endpoint); |
| EXPECT_FALSE(seen_endpoints.count(endpoint.info.url)); |
| seen_endpoints.insert(endpoint.info.url); |
| endpoint_manager_->InformOfEndpointRequest(NetworkAnonymizationKey(), |
| endpoint.info.url, false); |
| } |
| // All endpoints should now be marked as bad. |
| EXPECT_FALSE(endpoint_manager_->FindEndpointForDelivery(kGroupKey)); |
| |
| // Add another endpoint with a different NetworkAnonymizationKey; |
| const auto kDifferentNetworkAnonymizationKey = |
| NetworkAnonymizationKey::CreateSameSite(kSite); |
| const ReportingEndpointGroupKey kDifferentGroupKey( |
| kDifferentNetworkAnonymizationKey, kOrigin, kGroup); |
| SetEndpoint(kEndpoint, ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| ReportingEndpoint::EndpointInfo::kDefaultWeight, |
| kDifferentNetworkAnonymizationKey); |
| // All endpoints associated with the empty NetworkAnonymizationKey should |
| // still be marked as bad. |
| EXPECT_FALSE(endpoint_manager_->FindEndpointForDelivery(kGroupKey)); |
| |
| // Make the endpoint added for the kDifferentNetworkAnonymizationKey as bad. |
| endpoint_manager_->InformOfEndpointRequest(kDifferentNetworkAnonymizationKey, |
| kEndpoint, false); |
| // The only endpoint for kDifferentNetworkAnonymizationKey should still be |
| // marked as bad. |
| EXPECT_FALSE(endpoint_manager_->FindEndpointForDelivery(kDifferentGroupKey)); |
| // One of the endpoints for the empty NetworkAnonymizationKey should no longer |
| // be marked as bad, due to eviction. |
| ReportingEndpoint endpoint = |
| endpoint_manager_->FindEndpointForDelivery(kGroupKey); |
| EXPECT_TRUE(endpoint); |
| |
| // Reporting a success for the (only) good endpoint for the empty |
| // NetworkAnonymizationKey should evict the entry for |
| // kNetworkAnonymizationKey, since the most recent FindEndpointForDelivery() |
| // call visited all of the empty NetworkAnonymizationKey's cached bad entries. |
| endpoint_manager_->InformOfEndpointRequest(NetworkAnonymizationKey(), |
| endpoint.info.url, true); |
| |
| EXPECT_TRUE(endpoint_manager_->FindEndpointForDelivery(kDifferentGroupKey)); |
| } |
| |
| } // namespace |
| } // namespace net |