123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- // Copyright 2014 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 "components/domain_reliability/context.h"
- #include <algorithm>
- #include <limits>
- #include <utility>
- #include "base/bind.h"
- #include "base/json/json_writer.h"
- #include "base/logging.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/metrics/sparse_histogram.h"
- #include "base/rand_util.h"
- #include "base/values.h"
- #include "components/domain_reliability/dispatcher.h"
- #include "components/domain_reliability/uploader.h"
- #include "components/domain_reliability/util.h"
- #include "net/base/net_errors.h"
- namespace domain_reliability {
- // static
- const int DomainReliabilityContext::kMaxUploadDepthToSchedule = 1;
- // static
- const size_t DomainReliabilityContext::kMaxQueuedBeacons = 150;
- DomainReliabilityContext::DomainReliabilityContext(
- const MockableTime* time,
- const DomainReliabilityScheduler::Params& scheduler_params,
- const std::string& upload_reporter_string,
- const base::TimeTicks* last_network_change_time,
- const UploadAllowedCallback& upload_allowed_callback,
- DomainReliabilityDispatcher* dispatcher,
- DomainReliabilityUploader* uploader,
- std::unique_ptr<const DomainReliabilityConfig> config)
- : config_(std::move(config)),
- time_(time),
- upload_reporter_string_(upload_reporter_string),
- scheduler_(time,
- config_->collectors.size(),
- scheduler_params,
- base::BindRepeating(&DomainReliabilityContext::ScheduleUpload,
- base::Unretained(this))),
- dispatcher_(dispatcher),
- uploader_(uploader),
- uploading_beacons_size_(0),
- last_network_change_time_(last_network_change_time),
- upload_allowed_callback_(upload_allowed_callback) {}
- DomainReliabilityContext::~DomainReliabilityContext() {
- for (auto& beacon_ptr : beacons_) {
- beacon_ptr->outcome = DomainReliabilityBeacon::Outcome::kContextShutDown;
- }
- }
- void DomainReliabilityContext::OnBeacon(
- std::unique_ptr<DomainReliabilityBeacon> beacon) {
- bool success = (beacon->status == "ok");
- double sample_rate = beacon->details.quic_port_migration_detected
- ? 1.0
- : config().GetSampleRate(success);
- if (base::RandDouble() >= sample_rate)
- return;
- beacon->sample_rate = sample_rate;
- // Allow beacons about reports, but don't schedule an upload for more than
- // one layer of recursion, to avoid infinite report loops.
- if (beacon->upload_depth <= kMaxUploadDepthToSchedule)
- scheduler_.OnBeaconAdded();
- beacons_.push_back(std::move(beacon));
- bool should_evict = beacons_.size() > kMaxQueuedBeacons;
- if (should_evict)
- RemoveOldestBeacon();
- }
- void DomainReliabilityContext::ClearBeacons() {
- for (auto& beacon_ptr : beacons_) {
- beacon_ptr->outcome = DomainReliabilityBeacon::Outcome::kCleared;
- }
- beacons_.clear();
- uploading_beacons_size_ = 0;
- }
- void DomainReliabilityContext::GetQueuedBeaconsForTesting(
- std::vector<const DomainReliabilityBeacon*>* beacons_out) const {
- DCHECK(beacons_out);
- beacons_out->clear();
- for (const auto& beacon : beacons_)
- beacons_out->push_back(beacon.get());
- }
- void DomainReliabilityContext::ScheduleUpload(
- base::TimeDelta min_delay,
- base::TimeDelta max_delay) {
- dispatcher_->ScheduleTask(
- base::BindOnce(&DomainReliabilityContext::CallUploadAllowedCallback,
- weak_factory_.GetWeakPtr()),
- min_delay, max_delay);
- }
- void DomainReliabilityContext::CallUploadAllowedCallback() {
- RemoveExpiredBeacons();
- if (beacons_.empty())
- return;
- upload_allowed_callback_.Run(
- config().origin,
- base::BindOnce(&DomainReliabilityContext::OnUploadAllowedCallbackComplete,
- weak_factory_.GetWeakPtr()));
- }
- void DomainReliabilityContext::OnUploadAllowedCallbackComplete(bool allowed) {
- if (allowed)
- StartUpload();
- }
- void DomainReliabilityContext::StartUpload() {
- RemoveExpiredBeacons();
- if (beacons_.empty())
- return;
- // Find the first beacon with an `upload_depth` of at most
- // kMaxUploadDepthToSchedule, in preparation to create a report containing all
- // beacons with matching NetworkIsolationKeys.
- bool found_beacon_to_upload = false;
- for (const auto& beacon : beacons_) {
- if (beacon->upload_depth <= kMaxUploadDepthToSchedule) {
- uploading_beacons_network_isolation_key_ = beacon->network_isolation_key;
- found_beacon_to_upload = true;
- break;
- }
- }
- if (!found_beacon_to_upload)
- return;
- size_t collector_index = scheduler_.OnUploadStart();
- const GURL& collector_url = *config().collectors[collector_index];
- DCHECK(upload_time_.is_null());
- upload_time_ = time_->NowTicks();
- std::string report_json = "{}";
- int max_upload_depth = -1;
- bool wrote = base::JSONWriter::Write(
- CreateReport(upload_time_, collector_url, &max_upload_depth),
- &report_json);
- DCHECK(wrote);
- DCHECK_NE(-1, max_upload_depth);
- uploader_->UploadReport(
- report_json, max_upload_depth, collector_url,
- uploading_beacons_network_isolation_key_,
- base::BindOnce(&DomainReliabilityContext::OnUploadComplete,
- weak_factory_.GetWeakPtr()));
- }
- void DomainReliabilityContext::OnUploadComplete(
- const DomainReliabilityUploader::UploadResult& result) {
- if (result.is_success())
- CommitUpload();
- else
- RollbackUpload();
- scheduler_.OnUploadComplete(result);
- DCHECK(!upload_time_.is_null());
- last_upload_time_ = upload_time_;
- upload_time_ = base::TimeTicks();
- // If there are pending beacons with a low enough depth, inform the scheduler
- // - it's possible only some beacons were added because of NetworkIsolationKey
- // mismatches, rather than due to new beacons being created.
- if (GetMinBeaconUploadDepth() <= kMaxUploadDepthToSchedule)
- scheduler_.OnBeaconAdded();
- }
- base::Value DomainReliabilityContext::CreateReport(base::TimeTicks upload_time,
- const GURL& collector_url,
- int* max_upload_depth_out) {
- DCHECK_GT(beacons_.size(), 0u);
- DCHECK_EQ(0u, uploading_beacons_size_);
- int max_upload_depth = 0;
- base::Value beacons_value(base::Value::Type::LIST);
- for (const auto& beacon : beacons_) {
- // Only include beacons with a matching NetworkIsolationKey in the report.
- if (beacon->network_isolation_key !=
- uploading_beacons_network_isolation_key_) {
- continue;
- }
- beacons_value.Append(
- beacon->ToValue(upload_time, *last_network_change_time_, collector_url,
- config().path_prefixes));
- if (beacon->upload_depth > max_upload_depth)
- max_upload_depth = beacon->upload_depth;
- ++uploading_beacons_size_;
- }
- DCHECK_GT(uploading_beacons_size_, 0u);
- base::Value report_value(base::Value::Type::DICTIONARY);
- report_value.SetStringKey("reporter", upload_reporter_string_);
- report_value.SetKey("entries", std::move(beacons_value));
- *max_upload_depth_out = max_upload_depth;
- return report_value;
- }
- void DomainReliabilityContext::CommitUpload() {
- auto current = beacons_.begin();
- while (uploading_beacons_size_ > 0) {
- DCHECK(current != beacons_.end());
- auto last = current;
- ++current;
- if ((*last)->network_isolation_key ==
- uploading_beacons_network_isolation_key_) {
- (*last)->outcome = DomainReliabilityBeacon::Outcome::kUploaded;
- beacons_.erase(last);
- --uploading_beacons_size_;
- }
- }
- }
- void DomainReliabilityContext::RollbackUpload() {
- uploading_beacons_size_ = 0;
- }
- void DomainReliabilityContext::RemoveOldestBeacon() {
- DCHECK(!beacons_.empty());
- DVLOG(1) << "Beacon queue for " << config().origin << " full; "
- << "removing oldest beacon";
- // If the beacon being removed has a NetworkIsolationKey that matches that of
- // the current upload, decrement |uploading_beacons_size_|.
- if (uploading_beacons_size_ > 0 &&
- beacons_.front()->network_isolation_key ==
- uploading_beacons_network_isolation_key_) {
- --uploading_beacons_size_;
- }
- beacons_.front()->outcome = DomainReliabilityBeacon::Outcome::kEvicted;
- beacons_.pop_front();
- }
- void DomainReliabilityContext::RemoveExpiredBeacons() {
- base::TimeTicks now = time_->NowTicks();
- const base::TimeDelta kMaxAge = base::Hours(1);
- while (!beacons_.empty() && now - beacons_.front()->start_time >= kMaxAge) {
- beacons_.front()->outcome = DomainReliabilityBeacon::Outcome::kExpired;
- beacons_.pop_front();
- }
- }
- // Gets the minimum depth of all entries in |beacons_|.
- int DomainReliabilityContext::GetMinBeaconUploadDepth() const {
- int min = std::numeric_limits<int>::max();
- for (const auto& beacon : beacons_) {
- if (beacon->upload_depth < min)
- min = beacon->upload_depth;
- }
- return min;
- }
- } // namespace domain_reliability
|