File: report_uploader.cc

package info (click to toggle)
chromium 139.0.7258.138-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 6,120,676 kB
  • sloc: cpp: 35,100,869; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (174 lines) | stat: -rw-r--r-- 6,430 bytes parent folder | download | duplicates (5)
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