// 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_endpoint_manager.h" #include #include #include #include #include "base/logging.h" #include "base/macros.h" #include "base/rand_util.h" #include "base/stl_util.h" #include "base/time/tick_clock.h" #include "net/base/backoff_entry.h" #include "net/base/rand_callback.h" #include "net/reporting/reporting_cache.h" #include "net/reporting/reporting_client.h" #include "net/reporting/reporting_delegate.h" #include "net/reporting/reporting_policy.h" #include "url/gurl.h" #include "url/origin.h" namespace net { namespace { class ReportingEndpointManagerImpl : public ReportingEndpointManager { public: ReportingEndpointManagerImpl(ReportingContext* context, const RandIntCallback& rand_callback) : context_(context), rand_callback_(rand_callback) {} ~ReportingEndpointManagerImpl() override = default; const ReportingClient* FindClientForOriginAndGroup( const url::Origin& origin, const std::string& group) override { std::vector clients; cache()->GetClientsForOriginAndGroup(origin, group, &clients); // Highest-priority client(s) that are not expired, pending, failing, or // forbidden for use by the ReportingDelegate. std::vector available_clients; // Total weight of clients in available_clients. int total_weight = 0; base::TimeTicks now = tick_clock()->NowTicks(); for (const ReportingClient* client : clients) { if (client->expires < now) continue; if (base::ContainsKey(endpoint_backoff_, client->endpoint) && endpoint_backoff_[client->endpoint]->ShouldRejectRequest()) { continue; } if (!delegate()->CanUseClient(client->origin, client->endpoint)) continue; // If this client is lower priority than the ones we've found, skip it. if (!available_clients.empty() && client->priority > available_clients[0]->priority) { continue; } // If this client is higher priority than the ones we've found (or we // haven't found any), forget about those ones and remember this one. if (available_clients.empty() || client->priority < available_clients[0]->priority) { available_clients.clear(); total_weight = 0; } available_clients.push_back(client); total_weight += client->weight; } if (available_clients.empty()) { return nullptr; } int random_index = rand_callback_.Run(0, total_weight - 1); int weight_so_far = 0; for (size_t i = 0; i < available_clients.size(); ++i) { const ReportingClient* client = available_clients[i]; weight_so_far += client->weight; if (random_index < weight_so_far) { return client; } } // TODO(juliatuttle): Can we reach this in some weird overflow case? NOTREACHED(); return nullptr; } void InformOfEndpointRequest(const GURL& endpoint, bool succeeded) override { if (!base::ContainsKey(endpoint_backoff_, endpoint)) { endpoint_backoff_[endpoint] = std::make_unique( &policy().endpoint_backoff_policy, tick_clock()); } endpoint_backoff_[endpoint]->InformOfRequest(succeeded); } private: const ReportingPolicy& policy() { return context_->policy(); } const base::TickClock* tick_clock() { return context_->tick_clock(); } ReportingDelegate* delegate() { return context_->delegate(); } ReportingCache* cache() { return context_->cache(); } ReportingContext* context_; RandIntCallback rand_callback_; // Note: Currently the ReportingBrowsingDataRemover does not clear this data // because it's not persisted to disk. If it's ever persisted, it will need // to be cleared as well. std::map> endpoint_backoff_; DISALLOW_COPY_AND_ASSIGN(ReportingEndpointManagerImpl); }; } // namespace // static std::unique_ptr ReportingEndpointManager::Create( ReportingContext* context, const RandIntCallback& rand_callback) { return std::make_unique(context, rand_callback); } ReportingEndpointManager::~ReportingEndpointManager() = default; } // namespace net