Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(63)

Side by Side Diff: components/reporting/core/browser/reporting_manager.cc

Issue 2249213002: [OBSOLETE] Reporting: Initial implementation. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix ProfileImplIOData Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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/reporting/core/browser/reporting_manager.h"
6
7 #include "base/bind.h"
8 #include "base/json/json_writer.h"
9 #include "base/memory/ptr_util.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/time/default_tick_clock.h"
13 #include "components/reporting/core/browser/reporting_util.h"
14 #include "net/http/http_request_info.h"
15 #include "net/http/http_response_headers.h"
16 #include "net/http/http_response_info.h"
17 #include "net/url_request/url_fetcher.h"
18 #include "net/url_request/url_request_context_getter.h"
19
20 namespace {
21 const char kDefaultGroupName[] = "default";
22 } // namespace
23
24 namespace reporting {
25
26 ReportingManager::ReportingManager(
27 std::unique_ptr<ReportingUploader> uploader,
28 const base::Callback<bool(const GURL&)>& is_origin_secure)
29 : is_origin_secure_(is_origin_secure),
30 clock_(new base::DefaultTickClock()),
31 uploader_(std::move(uploader)) {}
32 ReportingManager::~ReportingManager() {}
33
34 void ReportingManager::QueueReport(std::unique_ptr<ReportingReport> report) {
35 report->timestamp = clock_->NowTicks();
36 report->attempts = 0;
37 cache_.EnqueueReport(std::move(report));
38 }
39
40 void ReportingManager::ProcessHeader(const GURL& origin,
41 const std::string& header_value) {
42 DCHECK(is_origin_secure_.Run(origin));
43
44 std::vector<std::string> errors;
45 std::vector<EndpointTuple> tuples =
46 EndpointTuple::FromHeader(header_value, is_origin_secure_, &errors);
47
48 // TODO: Plumb these out in a more modular way and expose them somewhere more
49 // visible?
Randy Smith (Not in Mondays) 2016/10/21 20:15:11 I'm trying to think what the right place would be.
Julia Tuttle 2016/11/02 20:44:39 The DevTools console, most likely.
50 for (const std::string& error : errors) {
51 LOG(WARNING) << "Origin " << origin.spec() << " sent "
52 << "Report-To header with error: " << error << ": "
53 << header_value;
54 }
55
56 for (auto& tuple : tuples)
57 ProcessEndpoint(origin, tuple);
58 }
59
60 void ReportingManager::SendReports() {
61 const ReportingCache::ReportSet& reports = cache_.GetReports();
62 const ReportingCache::EndpointMap& endpoints = cache_.GetEndpoints();
63
64 std::map<const std::unique_ptr<ReportingEndpoint>*,
65 std::vector<const std::unique_ptr<ReportingReport>*>>
66 endpoint_map;
67 for (auto& report : reports) {
68 for (auto& pair : endpoints) {
69 const std::unique_ptr<ReportingEndpoint>& endpoint = pair.second;
70 if (endpoint->is_expired(clock_->NowTicks()) ||
71 endpoint->backoff.ShouldRejectRequest() ||
72 !DoesEndpointMatchReport(*endpoint, *report)) {
73 continue;
74 }
75 endpoint_map[&endpoint].push_back(&report);
76 goto next_report;
Randy Smith (Not in Mondays) 2016/10/21 20:15:11 I think this matching-and-exit algorithm means tha
Randy Smith (Not in Mondays) 2016/10/21 20:15:11 Why not "break;"?
Julia Tuttle 2016/11/02 20:44:39 I think I was worried I'd end up breaking out of t
Julia Tuttle 2016/11/02 20:44:39 ...I *don't* think that's what the spec wants. *I*
77 }
78 next_report:;
79 }
80
81 for (auto& pair : endpoint_map) {
82 std::unique_ptr<Delivery> delivery(new Delivery(*pair.first, pair.second));
83 delivery->endpoint->last_used = clock_->NowTicks();
84 const GURL url = delivery->endpoint->url;
85 std::string json = SerializeReports(delivery->reports);
86 uploader_->AttemptDelivery(
87 url, json, base::Bind(&ReportingManager::OnDeliveryAttemptComplete,
88 base::Unretained(this), std::move(delivery)));
89 }
90 }
91
92 // static
93 bool ReportingManager::EndpointTuple::FromDictionary(
94 const base::DictionaryValue& dictionary,
95 EndpointTuple* tuple_out,
96 std::string* error_out) {
97 std::string url_string;
98 if (!dictionary.GetString("url", &url_string)) {
99 *error_out = "url missing or not a string";
100 return false;
101 }
102 tuple_out->url = GURL(url_string);
103
104 tuple_out->subdomains = false;
105 dictionary.GetBoolean("includeSubdomains", &tuple_out->subdomains);
106
107 int ttl_sec;
108 if (!dictionary.GetInteger("max-age", &ttl_sec)) {
109 *error_out = "max-age missing or not an integer";
110 return false;
111 }
112 tuple_out->ttl = base::TimeDelta::FromSeconds(ttl_sec);
113
114 tuple_out->group = kDefaultGroupName;
115 dictionary.GetString("group", &tuple_out->group);
116
117 return true;
118 }
119
120 // static
121 std::vector<ReportingManager::EndpointTuple>
122 ReportingManager::EndpointTuple::FromHeader(
123 const std::string& header,
124 const base::Callback<bool(const GURL&)>& is_origin_secure,
125 std::vector<std::string>* errors_out) {
126 std::vector<ReportingManager::EndpointTuple> tuples;
127
128 errors_out->clear();
129
130 std::unique_ptr<base::Value> value(ParseJFV(header));
131 if (!value) {
132 errors_out->push_back("failed to parse JSON field value.");
133 return tuples;
134 }
135
136 base::ListValue* list;
137 bool was_list = value->GetAsList(&list);
138 DCHECK(was_list);
139
140 base::DictionaryValue* item;
141 for (size_t i = 0; i < list->GetSize(); i++) {
142 std::string error_prefix = "endpoint " + base::SizeTToString(i + 1) +
143 " of " + base::SizeTToString(list->GetSize()) +
144 ":";
145 if (!list->GetDictionary(i, &item)) {
146 errors_out->push_back(error_prefix + "is not a dictionary.");
147 continue;
148 }
149 EndpointTuple tuple;
150 std::string error;
151 if (!EndpointTuple::FromDictionary(*item, &tuple, &error)) {
152 errors_out->push_back(error_prefix + error);
153 continue;
154 }
155 if (!is_origin_secure.Run(tuple.url)) {
156 errors_out->push_back(error_prefix + "url " + tuple.url.spec() +
157 " is insecure.");
158 continue;
159 }
160 if (tuple.ttl < base::TimeDelta()) {
161 errors_out->push_back(error_prefix + "ttl is negative.");
162 continue;
163 }
164 tuples.push_back(tuple);
165 }
166 return tuples;
167 }
168
169 std::string ReportingManager::EndpointTuple::ToString() const {
170 return "(url=" + url.spec() + ", subdomains=" +
171 (subdomains ? "true" : "false") + ", ttl=" +
172 base::Int64ToString(ttl.InSeconds()) + "s" + ", group=" + group + ")";
173 }
174
175 ReportingManager::Delivery::Delivery(
176 const std::unique_ptr<ReportingEndpoint>& endpoint,
177 const std::vector<const std::unique_ptr<ReportingReport>*>& reports)
178 : endpoint(endpoint), reports(reports) {}
179
180 ReportingManager::Delivery::~Delivery() {}
181
182 void ReportingManager::ProcessEndpoint(const GURL& origin,
183 const EndpointTuple& tuple) {
184 const std::unique_ptr<ReportingEndpoint>* endpoint_ptr =
185 cache_.GetEndpoint(tuple.url);
186 ReportingEndpoint* endpoint = endpoint_ptr ? endpoint_ptr->get() : nullptr;
187
188 if (endpoint)
189 endpoint->clients.erase(origin);
190
191 if (tuple.ttl <= base::TimeDelta())
192 return;
193
194 if (!endpoint) {
195 endpoint =
196 new ReportingEndpoint(tuple.url, backoff_policy_, clock_->NowTicks());
197 cache_.InsertEndpoint(base::WrapUnique(endpoint));
198 }
199
200 endpoint->clients.insert(std::make_pair(
201 GURL(origin), ReportingClient(origin, tuple.subdomains, tuple.group,
202 tuple.ttl, clock_->NowTicks())));
203 }
204
205 bool ReportingManager::DoesEndpointMatchReport(
206 const ReportingEndpoint& endpoint,
207 const ReportingReport& report) {
208 for (auto& pair : endpoint.clients) {
209 const ReportingClient& client = pair.second;
210 if (client.is_expired(clock_->NowTicks()))
211 continue;
212 if (!base::EqualsCaseInsensitiveASCII(client.group, report.group))
213 continue;
214 if (client.origin == report.origin)
215 return true;
216 if (client.subdomains && report.origin.DomainIs(client.origin.host_piece()))
217 return true;
218 }
219 return false;
220 }
221
222 void ReportingManager::OnDeliveryAttemptComplete(
223 const std::unique_ptr<Delivery>& delivery,
224 ReportingUploader::Outcome outcome) {
225 switch (outcome) {
226 case ReportingUploader::SUCCESS:
227 delivery->endpoint->backoff.InformOfRequest(true);
228 for (auto report_ptr : delivery->reports)
229 cache_.DequeueReport(*report_ptr);
230 break;
231 case ReportingUploader::FAILURE:
232 delivery->endpoint->backoff.InformOfRequest(false);
233 break;
234 case ReportingUploader::REMOVE_ENDPOINT:
235 // NOTE: This is not specified, but seems the obvious intention.
236 cache_.RemoveEndpoint(delivery->endpoint);
237 break;
238 }
239 }
240
241 std::string ReportingManager::SerializeReports(
242 const std::vector<const std::unique_ptr<ReportingReport>*>& reports) {
243 base::ListValue collection;
244 for (auto& report_ptr : reports) {
245 ReportingReport* report = report_ptr->get();
246 ++report->attempts;
247
248 std::unique_ptr<base::DictionaryValue> data(new base::DictionaryValue());
249 data->SetInteger("age",
250 (clock_->NowTicks() - report->timestamp).InMilliseconds());
251 data->SetString("type", report->type);
252 data->SetString("url", report->url.spec());
253 data->Set("report", report->body->DeepCopy());
254 collection.Append(std::move(data));
255 }
256
257 std::string json = "";
258 bool written = base::JSONWriter::Write(collection, &json);
259 DCHECK(written);
260 return json;
261 }
262
263 void ReportingManager::CollectGarbage() {
264 const base::TimeDelta kEndpointMaxUnusedTime = base::TimeDelta::FromDays(7);
265 const base::TimeDelta kReportMaxUndeliveredTime =
266 base::TimeDelta::FromDays(2);
267 const int kEndpointMaxFailures = 5;
268 const int kReportMaxFailures = 5;
269
270 // TODO: Histogram report and endpoint removal reasons and maybe ages.
271
272 const ReportingCache::EndpointMap& endpoints = cache_.GetEndpoints();
273 for (auto& pair : endpoints) {
274 const std::unique_ptr<ReportingEndpoint>& endpoint = pair.second;
275 if (endpoint->is_expired(clock_->NowTicks())) {
276 cache_.RemoveEndpoint(endpoint);
277 continue;
278 }
279 if (clock_->NowTicks() - endpoint->last_used > kEndpointMaxUnusedTime) {
280 cache_.RemoveEndpoint(endpoint);
281 continue;
282 }
283 if (endpoint->backoff.failure_count() > kEndpointMaxFailures) {
284 cache_.RemoveEndpoint(endpoint);
285 continue;
286 }
287 }
288
289 const ReportingCache::ReportSet& reports = cache_.GetReports();
290 for (auto& report : reports) {
291 if (report->attempts > kReportMaxFailures) {
292 cache_.DequeueReport(report);
293 continue;
294 }
295 if (clock_->NowTicks() - report->timestamp > kReportMaxUndeliveredTime) {
296 cache_.DequeueReport(report);
297 continue;
298 }
299 }
300 }
301
302 } // namespace reporting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698