1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/enterprise/browser/reporting/report_uploader.h"
#include <utility>
#include "base/metrics/histogram_functions.h"
#include "base/time/time.h"
#include "components/enterprise/browser/reporting/report_type.h"
#include "components/policy/core/common/cloud/cloud_policy_client.h"
#include "components/policy/core/common/policy_logger.h"
#include "components/policy/proto/device_management_backend.pb.h"
namespace em = enterprise_management;
namespace enterprise_reporting {
namespace {
// Retry starts with 1 minute delay and is doubled with every failure.
const net::BackoffEntry::Policy kDefaultReportUploadBackoffPolicy = {
0, // Number of initial errors to ignore before applying
// exponential back-off rules.
60000, // Initial delay is 60 seconds.
2, // Factor by which the waiting time will be multiplied.
0.1, // Fuzzing percentage.
-1, // No maximum delay.
-1, // It's up to the caller to reset the backoff time.
false // Do not always use initial delay.
};
void RecordReportResponseMetrics(ReportResponseMetricsStatus status) {
base::UmaHistogramEnumeration("Enterprise.CloudReportingResponse", status);
}
} // namespace
ReportUploader::ReportUploader(policy::CloudPolicyClient* client,
int maximum_number_of_retries)
: client_(client),
backoff_entry_(&kDefaultReportUploadBackoffPolicy),
maximum_number_of_retries_(maximum_number_of_retries) {}
ReportUploader::~ReportUploader() = default;
void ReportUploader::SetRequestAndUpload(const ReportGenerationConfig& config,
ReportRequestQueue requests,
ReportCallback callback) {
config_ = config;
requests_ = std::move(requests);
callback_ = std::move(callback);
Upload();
}
void ReportUploader::Upload() {
auto callback = base::BindRepeating(&ReportUploader::OnRequestFinished,
weak_ptr_factory_.GetWeakPtr());
switch (config_.report_type) {
case ReportType::kFull:
case ReportType::kBrowserVersion: {
auto request = std::make_unique<ReportRequest::DeviceReportRequestProto>(
requests_.front()->GetDeviceReportRequest());
// Because MessageLite does not support DebugMessage(), print
// serialize string for debugging purposes. It's a non-human-friendly
// binary string but still provide useful information.
VLOG(2) << "Uploading report: " << request->SerializeAsString();
#if BUILDFLAG(IS_CHROMEOS)
client_->UploadChromeOsUserReport(std::move(request),
std::move(callback));
#else
client_->UploadChromeDesktopReport(std::move(request),
std::move(callback));
#endif
break;
}
case ReportType::kProfileReport: {
auto request = std::make_unique<em::ChromeProfileReportRequest>(
requests_.front()->GetChromeProfileReportRequest());
VLOG(2) << "Uploading report: " << request->SerializeAsString();
if (config_.security_signals_mode != SecuritySignalsMode::kNoSignals) {
VLOG_POLICY(1, REPORTING)
<< "Uploading profile report with signals mode "
<< static_cast<int>(config_.security_signals_mode);
}
client_->UploadChromeProfileReport(
config_.use_cookies, std::move(request), std::move(callback));
break;
}
}
}
void ReportUploader::OnRequestFinished(
policy::CloudPolicyClient::Result result) {
// Crash if the client is not registered, this should not happen.
// TODO(b/256553070) Handle unregistered case without crashing.
CHECK(!result.IsClientNotRegisteredError());
if (result.IsSuccess()) {
NextRequest();
RecordReportResponseMetrics(ReportResponseMetricsStatus::kSuccess);
return;
}
switch (result.GetDMServerError()) {
case policy::DM_STATUS_REQUEST_FAILED: // network error
RecordReportResponseMetrics(ReportResponseMetricsStatus::kNetworkError);
Retry();
break;
case policy::DM_STATUS_TEMPORARY_UNAVAILABLE: // 5xx server error
RecordReportResponseMetrics(
ReportResponseMetricsStatus::kTemporaryServerError);
Retry();
break;
// DM_STATUS_SERVICE_DEVICE_ID_CONFLICT is caused by 409 conflict. It can
// be caused by either device id conflict or DDS concur error which is
// a database error. We only want to retry for the second case. However,
// there is no way for us to tell difference right now so we will retry
// regardless.
case policy::DM_STATUS_SERVICE_TOO_MANY_REQUESTS:
RecordReportResponseMetrics(
ReportResponseMetricsStatus::kDDSConcurrencyError);
Retry();
break;
case policy::DM_STATUS_REQUEST_TOO_LARGE:
// Treats the REQUEST_TOO_LARGE error as a success upload. It's likely
// a calculation error during request generating and there is nothing
// can be done here.
RecordReportResponseMetrics(
ReportResponseMetricsStatus::kRequestTooLargeError);
NextRequest();
break;
default:
RecordReportResponseMetrics(ReportResponseMetricsStatus::kOtherError);
SendResponse(ReportStatus::kPersistentError);
break;
}
}
void ReportUploader::Retry() {
backoff_entry_.InformOfRequest(false);
// We have retried enough, time to give up.
if (HasRetriedTooOften()) {
SendResponse(ReportStatus::kTransientError);
return;
}
backoff_request_timer_.Start(
FROM_HERE, backoff_entry_.GetTimeUntilRelease(),
base::BindOnce(&ReportUploader::Upload, weak_ptr_factory_.GetWeakPtr()));
}
bool ReportUploader::HasRetriedTooOften() {
return backoff_entry_.failure_count() > maximum_number_of_retries_;
}
void ReportUploader::SendResponse(const ReportStatus status) {
std::move(callback_).Run(status);
}
void ReportUploader::NextRequest() {
// We don't reset the backoff in case there are multiple requests in a row
// and we don't start from 1 minute again.
backoff_entry_.InformOfRequest(true);
requests_.pop();
if (requests_.size() == 0)
SendResponse(ReportStatus::kSuccess);
else
Upload();
return;
}
} // namespace enterprise_reporting
|