blob: 20f5d86a80038be882aaec084c22084916dba0f8 [file] [log] [blame]
[email protected]2a172e42014-02-21 04:06:101// 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/rappor/log_uploader.h"
6
avif57136c12015-12-25 23:27:457#include <stddef.h>
8#include <stdint.h>
dcheng51ace48a2015-12-26 22:45:179#include <utility>
avif57136c12015-12-25 23:27:4510
Sebastien Marchand53801a32019-01-25 16:26:1111#include "base/bind.h"
Ilya Sherman1edb6f182017-12-12 04:00:4212#include "base/metrics/histogram_functions.h"
asvitkine454600f2015-06-16 16:34:5013#include "base/metrics/histogram_macros.h"
[email protected]2a172e42014-02-21 04:06:1014#include "net/base/load_flags.h"
[email protected]f30f4832014-05-07 15:29:5015#include "net/base/net_errors.h"
rhalavati20a32db2017-03-08 11:02:4916#include "net/traffic_annotation/network_traffic_annotation.h"
Mark Pilgrimedc1d15632018-05-22 17:38:2317#include "services/network/public/cpp/resource_request.h"
18#include "services/network/public/cpp/shared_url_loader_factory.h"
19#include "services/network/public/cpp/simple_url_loader.h"
[email protected]2a172e42014-02-21 04:06:1020
21namespace {
22
23// The delay, in seconds, between uploading when there are queued logs to send.
24const int kUnsentLogsIntervalSeconds = 3;
25
26// When uploading metrics to the server fails, we progressively wait longer and
27// longer before sending the next log. This backoff process helps reduce load
28// on a server that is having issues.
29// The following is the multiplier we use to expand that inter-log duration.
30const double kBackoffMultiplier = 1.1;
31
32// The maximum backoff multiplier.
33const int kMaxBackoffIntervalSeconds = 60 * 60;
34
35// The maximum number of unsent logs we will keep.
36// TODO(holte): Limit based on log size instead.
37const size_t kMaxQueuedLogs = 10;
38
39enum DiscardReason {
40 UPLOAD_SUCCESS,
41 UPLOAD_REJECTED,
42 QUEUE_OVERFLOW,
43 NUM_DISCARD_REASONS
44};
45
holte5a7ed7c2015-01-09 23:52:4646void RecordDiscardReason(DiscardReason reason) {
47 UMA_HISTOGRAM_ENUMERATION("Rappor.DiscardReason",
48 reason,
49 NUM_DISCARD_REASONS);
50}
51
[email protected]2a172e42014-02-21 04:06:1052} // namespace
53
54namespace rappor {
55
Mark Pilgrimedc1d15632018-05-22 17:38:2356LogUploader::LogUploader(
57 const GURL& server_url,
58 const std::string& mime_type,
59 scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
[email protected]2a172e42014-02-21 04:06:1060 : server_url_(server_url),
61 mime_type_(mime_type),
Mark Pilgrimedc1d15632018-05-22 17:38:2362 url_loader_factory_(std::move(url_loader_factory)),
holte5a7ed7c2015-01-09 23:52:4663 is_running_(false),
[email protected]2a172e42014-02-21 04:06:1064 has_callback_pending_(false),
Mark Pilgrimedc1d15632018-05-22 17:38:2365 upload_interval_(
66 base::TimeDelta::FromSeconds(kUnsentLogsIntervalSeconds)) {}
[email protected]2a172e42014-02-21 04:06:1067
68LogUploader::~LogUploader() {}
69
holte5a7ed7c2015-01-09 23:52:4670void LogUploader::Start() {
71 is_running_ = true;
72 StartScheduledUpload();
73}
74
75void LogUploader::Stop() {
76 is_running_ = false;
77 // Rather than interrupting the current upload, just let it finish/fail and
78 // then inhibit any retry attempts.
79}
80
[email protected]2a172e42014-02-21 04:06:1081void LogUploader::QueueLog(const std::string& log) {
82 queued_logs_.push(log);
holte5a7ed7c2015-01-09 23:52:4683 // Don't drop logs yet if an upload is in progress. They will be dropped
84 // when it finishes.
85 if (!has_callback_pending_)
86 DropExcessLogs();
87 StartScheduledUpload();
88}
89
90void LogUploader::DropExcessLogs() {
91 while (queued_logs_.size() > kMaxQueuedLogs) {
92 DVLOG(2) << "Dropping excess log.";
93 RecordDiscardReason(QUEUE_OVERFLOW);
94 queued_logs_.pop();
95 }
[email protected]2a172e42014-02-21 04:06:1096}
97
98bool LogUploader::IsUploadScheduled() const {
99 return upload_timer_.IsRunning();
100}
101
102void LogUploader::ScheduleNextUpload(base::TimeDelta interval) {
[email protected]2a172e42014-02-21 04:06:10103 upload_timer_.Start(
104 FROM_HERE, interval, this, &LogUploader::StartScheduledUpload);
105}
106
holte5a7ed7c2015-01-09 23:52:46107bool LogUploader::CanStartUpload() const {
108 return is_running_ &&
109 !queued_logs_.empty() &&
110 !IsUploadScheduled() &&
111 !has_callback_pending_;
112}
113
[email protected]2a172e42014-02-21 04:06:10114void LogUploader::StartScheduledUpload() {
holte5a7ed7c2015-01-09 23:52:46115 if (!CanStartUpload())
116 return;
117 DVLOG(2) << "Upload to " << server_url_.spec() << " starting.";
[email protected]2a172e42014-02-21 04:06:10118 has_callback_pending_ = true;
rhalavati20a32db2017-03-08 11:02:49119 net::NetworkTrafficAnnotationTag traffic_annotation =
120 net::DefineNetworkTrafficAnnotation("rappor_report", R"(
121 semantics {
122 sender: "RAPPOR"
123 description:
124 "This service sends RAPPOR anonymous usage statistics to Google."
125 trigger:
126 "Reports are automatically generated on startup and at intervals "
127 "while Chromium is running."
128 data: "A protocol buffer with RAPPOR anonymous usage statistics."
129 destination: GOOGLE_OWNED_SERVICE
130 }
131 policy {
Ramin Halavati3b979782017-07-21 11:40:26132 cookies_allowed: NO
rhalavati20a32db2017-03-08 11:02:49133 setting:
134 "Users can enable or disable this feature by stopping "
135 "'Automatically send usage statistics and crash reports to Google'"
136 "in Chromium's settings under Advanced Settings, Privacy. The "
137 "feature is enabled by default."
rhalavatieaa64e92017-04-03 09:36:43138 chrome_policy {
rhalavati20a32db2017-03-08 11:02:49139 MetricsReportingEnabled {
140 policy_options {mode: MANDATORY}
141 MetricsReportingEnabled: false
142 }
143 }
144 })");
[email protected]2a172e42014-02-21 04:06:10145
Mark Pilgrimedc1d15632018-05-22 17:38:23146 auto resource_request = std::make_unique<network::ResourceRequest>();
147 resource_request->url = server_url_;
[email protected]2a172e42014-02-21 04:06:10148 // We already drop cookies server-side, but we might as well strip them out
149 // client-side as well.
Yutaka Hirano3d804982019-07-29 06:41:34150 resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
Mark Pilgrimedc1d15632018-05-22 17:38:23151 resource_request->method = "POST";
152 simple_url_loader_ = network::SimpleURLLoader::Create(
153 std::move(resource_request), traffic_annotation);
154 simple_url_loader_->AttachStringForUpload(queued_logs_.front(), mime_type_);
155 // TODO re-add data use measurement once SimpleURLLoader supports it.
156 // ID=data_use_measurement::DataUseUserData::RAPPOR
157 simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
158 url_loader_factory_.get(),
159 base::BindOnce(&LogUploader::OnSimpleLoaderComplete,
160 base::Unretained(this)));
[email protected]2a172e42014-02-21 04:06:10161}
162
163// static
164base::TimeDelta LogUploader::BackOffUploadInterval(base::TimeDelta interval) {
165 DCHECK_GT(kBackoffMultiplier, 1.0);
avif57136c12015-12-25 23:27:45166 interval = base::TimeDelta::FromMicroseconds(
167 static_cast<int64_t>(kBackoffMultiplier * interval.InMicroseconds()));
[email protected]2a172e42014-02-21 04:06:10168
169 base::TimeDelta max_interval =
170 base::TimeDelta::FromSeconds(kMaxBackoffIntervalSeconds);
171 return interval > max_interval ? max_interval : interval;
172}
173
Mark Pilgrimedc1d15632018-05-22 17:38:23174void LogUploader::OnSimpleLoaderComplete(
175 std::unique_ptr<std::string> response_body) {
176 int response_code = -1;
177 if (simple_url_loader_->ResponseInfo() &&
178 simple_url_loader_->ResponseInfo()->headers) {
179 response_code =
180 simple_url_loader_->ResponseInfo()->headers->response_code();
181 }
holte5a7ed7c2015-01-09 23:52:46182 DVLOG(2) << "Upload fetch complete response code: " << response_code;
[email protected]2a172e42014-02-21 04:06:10183
Mark Pilgrimedc1d15632018-05-22 17:38:23184 int net_error = simple_url_loader_->NetError();
185 if (net_error != net::OK && (response_code == -1 || response_code == 200)) {
186 base::UmaHistogramSparse("Rappor.FailedUploadErrorCode", -net_error);
187 DVLOG(1) << "Rappor server upload failed with error: " << net_error << ": "
188 << net::ErrorToString(net_error);
[email protected]f30f4832014-05-07 15:29:50189 } else {
190 // Log a histogram to track response success vs. failure rates.
Ilya Sherman1edb6f182017-12-12 04:00:42191 base::UmaHistogramSparse("Rappor.UploadResponseCode", response_code);
[email protected]f30f4832014-05-07 15:29:50192 }
[email protected]2a172e42014-02-21 04:06:10193
Mark Pilgrimedc1d15632018-05-22 17:38:23194 const bool upload_succeeded = !!response_body;
[email protected]2a172e42014-02-21 04:06:10195
196 // Determine whether this log should be retransmitted.
197 DiscardReason reason = NUM_DISCARD_REASONS;
198 if (upload_succeeded) {
199 reason = UPLOAD_SUCCESS;
200 } else if (response_code == 400) {
201 reason = UPLOAD_REJECTED;
[email protected]2a172e42014-02-21 04:06:10202 }
203
204 if (reason != NUM_DISCARD_REASONS) {
holte5a7ed7c2015-01-09 23:52:46205 DVLOG(2) << "Log discarded.";
206 RecordDiscardReason(reason);
[email protected]2a172e42014-02-21 04:06:10207 queued_logs_.pop();
208 }
209
holte5a7ed7c2015-01-09 23:52:46210 DropExcessLogs();
211
[email protected]2a172e42014-02-21 04:06:10212 // Error 400 indicates a problem with the log, not with the server, so
213 // don't consider that a sign that the server is in trouble.
[email protected]ccb49262014-03-26 04:10:17214 const bool server_is_healthy = upload_succeeded || response_code == 400;
holte5a7ed7c2015-01-09 23:52:46215 OnUploadFinished(server_is_healthy);
[email protected]2a172e42014-02-21 04:06:10216}
217
holte5a7ed7c2015-01-09 23:52:46218void LogUploader::OnUploadFinished(bool server_is_healthy) {
[email protected]2a172e42014-02-21 04:06:10219 DCHECK(has_callback_pending_);
220 has_callback_pending_ = false;
221 // If the server is having issues, back off. Otherwise, reset to default.
222 if (!server_is_healthy)
223 upload_interval_ = BackOffUploadInterval(upload_interval_);
224 else
225 upload_interval_ = base::TimeDelta::FromSeconds(kUnsentLogsIntervalSeconds);
226
holte5a7ed7c2015-01-09 23:52:46227 if (CanStartUpload())
[email protected]2a172e42014-02-21 04:06:10228 ScheduleNextUpload(upload_interval_);
229}
230
231} // namespace rappor