1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/domain_reliability/context.h"
6 
7 #include <algorithm>
8 #include <utility>
9 
10 #include "base/bind.h"
11 #include "base/json/json_writer.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/metrics/sparse_histogram.h"
15 #include "base/rand_util.h"
16 #include "base/values.h"
17 #include "components/domain_reliability/dispatcher.h"
18 #include "components/domain_reliability/uploader.h"
19 #include "components/domain_reliability/util.h"
20 #include "net/base/net_errors.h"
21 
22 using base::DictionaryValue;
23 using base::ListValue;
24 using base::Value;
25 
26 namespace domain_reliability {
27 
28 // static
29 const int DomainReliabilityContext::kMaxUploadDepthToSchedule = 1;
30 
31 // static
32 const size_t DomainReliabilityContext::kMaxQueuedBeacons = 150;
33 
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)34 DomainReliabilityContext::DomainReliabilityContext(
35     const MockableTime* time,
36     const DomainReliabilityScheduler::Params& scheduler_params,
37     const std::string& upload_reporter_string,
38     const base::TimeTicks* last_network_change_time,
39     const UploadAllowedCallback& upload_allowed_callback,
40     DomainReliabilityDispatcher* dispatcher,
41     DomainReliabilityUploader* uploader,
42     std::unique_ptr<const DomainReliabilityConfig> config)
43     : config_(std::move(config)),
44       time_(time),
45       upload_reporter_string_(upload_reporter_string),
46       scheduler_(time,
47                  config_->collectors.size(),
48                  scheduler_params,
49                  base::BindRepeating(&DomainReliabilityContext::ScheduleUpload,
50                                      base::Unretained(this))),
51       dispatcher_(dispatcher),
52       uploader_(uploader),
53       uploading_beacons_size_(0),
54       last_network_change_time_(last_network_change_time),
55       upload_allowed_callback_(upload_allowed_callback) {}
56 
57 DomainReliabilityContext::~DomainReliabilityContext() = default;
58 
OnBeacon(std::unique_ptr<DomainReliabilityBeacon> beacon)59 void DomainReliabilityContext::OnBeacon(
60     std::unique_ptr<DomainReliabilityBeacon> beacon) {
61   bool success = (beacon->status == "ok");
62   double sample_rate = beacon->details.quic_port_migration_detected
63                            ? 1.0
64                            : config().GetSampleRate(success);
65   if (base::RandDouble() >= sample_rate)
66     return;
67   beacon->sample_rate = sample_rate;
68 
69   // Allow beacons about reports, but don't schedule an upload for more than
70   // one layer of recursion, to avoid infinite report loops.
71   if (beacon->upload_depth <= kMaxUploadDepthToSchedule)
72     scheduler_.OnBeaconAdded();
73   beacons_.push_back(std::move(beacon));
74   bool should_evict = beacons_.size() > kMaxQueuedBeacons;
75   if (should_evict)
76     RemoveOldestBeacon();
77 }
78 
ClearBeacons()79 void DomainReliabilityContext::ClearBeacons() {
80   beacons_.clear();
81   uploading_beacons_size_ = 0;
82 }
83 
GetWebUIData() const84 std::unique_ptr<Value> DomainReliabilityContext::GetWebUIData() const {
85   DictionaryValue* context_value = new DictionaryValue();
86 
87   context_value->SetString("origin", config().origin.spec());
88   context_value->SetInteger("beacon_count", static_cast<int>(beacons_.size()));
89   context_value->SetInteger("uploading_beacon_count",
90       static_cast<int>(uploading_beacons_size_));
91   context_value->Set("scheduler", scheduler_.GetWebUIData());
92 
93   return std::unique_ptr<Value>(context_value);
94 }
95 
GetQueuedBeaconsForTesting(std::vector<const DomainReliabilityBeacon * > * beacons_out) const96 void DomainReliabilityContext::GetQueuedBeaconsForTesting(
97     std::vector<const DomainReliabilityBeacon*>* beacons_out) const {
98   DCHECK(beacons_out);
99   beacons_out->clear();
100   for (const auto& beacon : beacons_)
101     beacons_out->push_back(beacon.get());
102 }
103 
ScheduleUpload(base::TimeDelta min_delay,base::TimeDelta max_delay)104 void DomainReliabilityContext::ScheduleUpload(
105     base::TimeDelta min_delay,
106     base::TimeDelta max_delay) {
107   dispatcher_->ScheduleTask(
108       base::BindOnce(&DomainReliabilityContext::CallUploadAllowedCallback,
109                      weak_factory_.GetWeakPtr()),
110       min_delay, max_delay);
111 }
112 
CallUploadAllowedCallback()113 void DomainReliabilityContext::CallUploadAllowedCallback() {
114   RemoveExpiredBeacons();
115   if (beacons_.empty())
116     return;
117 
118   upload_allowed_callback_.Run(
119       config().origin,
120       base::BindOnce(&DomainReliabilityContext::OnUploadAllowedCallbackComplete,
121                      weak_factory_.GetWeakPtr()));
122 }
123 
OnUploadAllowedCallbackComplete(bool allowed)124 void DomainReliabilityContext::OnUploadAllowedCallbackComplete(bool allowed) {
125   if (allowed)
126     StartUpload();
127 }
128 
StartUpload()129 void DomainReliabilityContext::StartUpload() {
130   RemoveExpiredBeacons();
131   if (beacons_.empty())
132     return;
133 
134   MarkUpload();
135 
136   size_t collector_index = scheduler_.OnUploadStart();
137   const GURL& collector_url = *config().collectors[collector_index];
138 
139   DCHECK(upload_time_.is_null());
140   upload_time_ = time_->NowTicks();
141   std::string report_json = "{}";
142   int max_upload_depth = -1;
143   bool wrote = base::JSONWriter::Write(
144       *CreateReport(upload_time_,
145                     collector_url,
146                     &max_upload_depth),
147                     &report_json);
148   DCHECK(wrote);
149   DCHECK_NE(-1, max_upload_depth);
150 
151   uploader_->UploadReport(
152       report_json, max_upload_depth, collector_url,
153       base::BindOnce(&DomainReliabilityContext::OnUploadComplete,
154                      weak_factory_.GetWeakPtr()));
155 }
156 
OnUploadComplete(const DomainReliabilityUploader::UploadResult & result)157 void DomainReliabilityContext::OnUploadComplete(
158     const DomainReliabilityUploader::UploadResult& result) {
159   if (result.is_success())
160     CommitUpload();
161   else
162     RollbackUpload();
163   scheduler_.OnUploadComplete(result);
164   DCHECK(!upload_time_.is_null());
165   last_upload_time_ = upload_time_;
166   upload_time_ = base::TimeTicks();
167 }
168 
CreateReport(base::TimeTicks upload_time,const GURL & collector_url,int * max_upload_depth_out) const169 std::unique_ptr<const Value> DomainReliabilityContext::CreateReport(
170     base::TimeTicks upload_time,
171     const GURL& collector_url,
172     int* max_upload_depth_out) const {
173   int max_upload_depth = 0;
174 
175   std::unique_ptr<ListValue> beacons_value(new ListValue());
176   for (const auto& beacon : beacons_) {
177     beacons_value->Append(beacon->ToValue(upload_time,
178                                           *last_network_change_time_,
179                                           collector_url,
180                                           config().path_prefixes));
181     if (beacon->upload_depth > max_upload_depth)
182       max_upload_depth = beacon->upload_depth;
183   }
184 
185   std::unique_ptr<DictionaryValue> report_value(new DictionaryValue());
186   report_value->SetString("reporter", upload_reporter_string_);
187   report_value->Set("entries", std::move(beacons_value));
188 
189   *max_upload_depth_out = max_upload_depth;
190   return std::move(report_value);
191 }
192 
MarkUpload()193 void DomainReliabilityContext::MarkUpload() {
194   DCHECK_EQ(0u, uploading_beacons_size_);
195   uploading_beacons_size_ = beacons_.size();
196   DCHECK_NE(0u, uploading_beacons_size_);
197 }
198 
CommitUpload()199 void DomainReliabilityContext::CommitUpload() {
200   auto begin = beacons_.begin();
201   auto end = begin + uploading_beacons_size_;
202   beacons_.erase(begin, end);
203   DCHECK_NE(0u, uploading_beacons_size_);
204   uploading_beacons_size_ = 0;
205 }
206 
RollbackUpload()207 void DomainReliabilityContext::RollbackUpload() {
208   DCHECK_NE(0u, uploading_beacons_size_);
209   uploading_beacons_size_ = 0;
210 }
211 
RemoveOldestBeacon()212 void DomainReliabilityContext::RemoveOldestBeacon() {
213   DCHECK(!beacons_.empty());
214 
215   DVLOG(1) << "Beacon queue for " << config().origin << " full; "
216            << "removing oldest beacon";
217 
218   beacons_.pop_front();
219 
220   // If that just removed a beacon counted in uploading_beacons_size_, decrement
221   // that.
222   if (uploading_beacons_size_ > 0)
223     --uploading_beacons_size_;
224 }
225 
RemoveExpiredBeacons()226 void DomainReliabilityContext::RemoveExpiredBeacons() {
227   base::TimeTicks now = time_->NowTicks();
228   const base::TimeDelta kMaxAge = base::TimeDelta::FromHours(1);
229   while (!beacons_.empty() && now - beacons_.front()->start_time >= kMaxAge)
230     beacons_.pop_front();
231 }
232 
233 }  // namespace domain_reliability
234