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