feedback_uploader.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  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. #include "components/feedback/feedback_uploader.h"
  5. #include "base/bind.h"
  6. #include "base/callback.h"
  7. #include "base/command_line.h"
  8. #include "base/metrics/histogram_macros.h"
  9. #include "base/task/task_traits.h"
  10. #include "base/task/thread_pool.h"
  11. #include "components/feedback/feedback_report.h"
  12. #include "components/feedback/feedback_switches.h"
  13. #include "components/variations/net/variations_http_headers.h"
  14. #include "net/base/load_flags.h"
  15. #include "services/network/public/cpp/resource_request.h"
  16. #include "services/network/public/cpp/simple_url_loader.h"
  17. #include "services/network/public/mojom/url_response_head.mojom.h"
  18. namespace feedback {
  19. namespace {
  20. constexpr char kReportSendingResultHistogramName[] =
  21. "Feedback.ReportSending.Result";
  22. // These values are persisted to logs. Entries should not be renumbered and
  23. // numeric values should never be reused.
  24. enum class FeedbackReportSendingResult {
  25. kSuccessAtFirstTry = 0, // The report was uploaded successfully without retry
  26. kSuccessAfterRetry = 1, // The report was uploaded successfully after retry
  27. kDropped = 2, // The report is corrupt or invalid and was dropped
  28. kMaxValue = kDropped,
  29. };
  30. constexpr base::FilePath::CharType kFeedbackReportPath[] =
  31. FILE_PATH_LITERAL("Feedback Reports");
  32. constexpr char kFeedbackPostUrl[] =
  33. "https://www.google.com/tools/feedback/chrome/__submit";
  34. constexpr char kProtoBufMimeType[] = "application/x-protobuf";
  35. constexpr int kHttpPostSuccessNoContent = 204;
  36. constexpr int kHttpPostFailNoConnection = -1;
  37. constexpr int kHttpPostFailClientError = 400;
  38. constexpr int kHttpPostFailServerError = 500;
  39. // The minimum time to wait before uploading reports are retried. Exponential
  40. // backoff delay is applied on successive failures.
  41. // This value can be overriden by tests by calling
  42. // FeedbackUploader::SetMinimumRetryDelayForTesting().
  43. base::TimeDelta g_minimum_retry_delay = base::Minutes(60);
  44. // If a new report is queued to be dispatched immediately while another is being
  45. // dispatched, this is the time to wait for the on-going dispatching to finish.
  46. base::TimeDelta g_dispatching_wait_delay = base::Seconds(4);
  47. GURL GetFeedbackPostGURL() {
  48. const base::CommandLine& command_line =
  49. *base::CommandLine::ForCurrentProcess();
  50. return GURL(command_line.HasSwitch(switches::kFeedbackServer)
  51. ? command_line.GetSwitchValueASCII(switches::kFeedbackServer)
  52. : kFeedbackPostUrl);
  53. }
  54. // Creates a new SingleThreadTaskRunner that is used to run feedback blocking
  55. // background work.
  56. scoped_refptr<base::SingleThreadTaskRunner> CreateUploaderTaskRunner() {
  57. // Uses a BLOCK_SHUTDOWN file task runner to prevent losing reports or
  58. // corrupting report's files.
  59. return base::ThreadPool::CreateSingleThreadTaskRunner(
  60. {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
  61. base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
  62. }
  63. } // namespace
  64. FeedbackUploader::FeedbackUploader(
  65. bool is_off_the_record,
  66. const base::FilePath& state_path,
  67. SharedURLLoaderFactoryGetter shared_url_loader_factory_getter)
  68. : FeedbackUploader(is_off_the_record,
  69. state_path,
  70. std::move(shared_url_loader_factory_getter),
  71. nullptr) {}
  72. FeedbackUploader::FeedbackUploader(
  73. bool is_off_the_record,
  74. const base::FilePath& state_path,
  75. scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory)
  76. : FeedbackUploader(is_off_the_record,
  77. state_path,
  78. SharedURLLoaderFactoryGetter(),
  79. shared_url_loader_factory) {}
  80. FeedbackUploader::~FeedbackUploader() {}
  81. // static
  82. void FeedbackUploader::SetMinimumRetryDelayForTesting(base::TimeDelta delay) {
  83. g_minimum_retry_delay = delay;
  84. }
  85. void FeedbackUploader::QueueReport(std::unique_ptr<std::string> data,
  86. bool has_email) {
  87. reports_queue_.emplace(base::MakeRefCounted<FeedbackReport>(
  88. feedback_reports_path_, base::Time::Now(), std::move(data), task_runner_,
  89. has_email));
  90. UpdateUploadTimer();
  91. }
  92. void FeedbackUploader::RequeueReport(scoped_refptr<FeedbackReport> report) {
  93. DCHECK_EQ(task_runner_, report->reports_task_runner());
  94. report->set_upload_at(base::Time::Now());
  95. reports_queue_.emplace(std::move(report));
  96. UpdateUploadTimer();
  97. }
  98. void FeedbackUploader::StartDispatchingReport() {
  99. DispatchReport();
  100. }
  101. void FeedbackUploader::OnReportUploadSuccess() {
  102. if (retry_delay_ == g_minimum_retry_delay) {
  103. UMA_HISTOGRAM_ENUMERATION(kReportSendingResultHistogramName,
  104. FeedbackReportSendingResult::kSuccessAtFirstTry);
  105. } else {
  106. UMA_HISTOGRAM_ENUMERATION(kReportSendingResultHistogramName,
  107. FeedbackReportSendingResult::kSuccessAfterRetry);
  108. }
  109. retry_delay_ = g_minimum_retry_delay;
  110. is_dispatching_ = false;
  111. // Explicitly release the successfully dispatched report.
  112. report_being_dispatched_->DeleteReportOnDisk();
  113. report_being_dispatched_ = nullptr;
  114. UpdateUploadTimer();
  115. }
  116. void FeedbackUploader::OnReportUploadFailure(bool should_retry) {
  117. if (should_retry) {
  118. // Implement a backoff delay by doubling the retry delay on each failure.
  119. retry_delay_ *= 2;
  120. report_being_dispatched_->set_upload_at(retry_delay_ + base::Time::Now());
  121. reports_queue_.emplace(report_being_dispatched_);
  122. } else {
  123. // The report won't be retried, hence explicitly delete its file on disk.
  124. report_being_dispatched_->DeleteReportOnDisk();
  125. UMA_HISTOGRAM_ENUMERATION(kReportSendingResultHistogramName,
  126. FeedbackReportSendingResult::kDropped);
  127. }
  128. // The report dispatching failed, and should either be retried or not. In all
  129. // cases, we need to release |report_being_dispatched_|. If it was up for
  130. // retry, then it has already been re-enqueued and will be kept alive.
  131. // Otherwise we're done with it and it should destruct.
  132. report_being_dispatched_ = nullptr;
  133. is_dispatching_ = false;
  134. UpdateUploadTimer();
  135. }
  136. bool FeedbackUploader::ReportsUploadTimeComparator::operator()(
  137. const scoped_refptr<FeedbackReport>& a,
  138. const scoped_refptr<FeedbackReport>& b) const {
  139. return a->upload_at() > b->upload_at();
  140. }
  141. FeedbackUploader::FeedbackUploader(
  142. bool is_off_the_record,
  143. const base::FilePath& state_path,
  144. SharedURLLoaderFactoryGetter url_loader_factory_getter,
  145. scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
  146. : url_loader_factory_getter_(std::move(url_loader_factory_getter)),
  147. url_loader_factory_(url_loader_factory),
  148. feedback_reports_path_(state_path.Append(kFeedbackReportPath)),
  149. task_runner_(CreateUploaderTaskRunner()),
  150. feedback_post_url_(GetFeedbackPostGURL()),
  151. retry_delay_(g_minimum_retry_delay),
  152. is_off_the_record_(is_off_the_record) {
  153. DCHECK(!!url_loader_factory_getter_ != !!url_loader_factory_);
  154. }
  155. void FeedbackUploader::AppendExtraHeadersToUploadRequest(
  156. network::ResourceRequest* resource_request) {}
  157. void FeedbackUploader::DispatchReport() {
  158. net::NetworkTrafficAnnotationTag traffic_annotation =
  159. net::DefineNetworkTrafficAnnotation("chrome_feedback_report_app", R"(
  160. semantics {
  161. sender: "Chrome Feedback Report App"
  162. description:
  163. "Users can press Alt+Shift+i to report a bug or a feedback in "
  164. "general. Along with the free-form text they entered, system logs "
  165. "that helps in diagnosis of the issue are sent to Google. This "
  166. "service uploads the report to Google Feedback server."
  167. trigger:
  168. "When user chooses to send a feedback to Google."
  169. data:
  170. "The free-form text that user has entered and useful debugging "
  171. "logs (UI logs, Chrome logs, kernel logs, auto update engine logs, "
  172. "ARC++ logs, etc.). The logs are redacted to remove any "
  173. "user-private data. The user can view the system information "
  174. "before sending, and choose to send the feedback report without "
  175. "system information and the logs (unchecking 'Send system "
  176. "information' prevents sending logs as well), the screenshot, or "
  177. "even his/her email address."
  178. destination: GOOGLE_OWNED_SERVICE
  179. }
  180. policy {
  181. cookies_allowed: NO
  182. setting:
  183. "This feature cannot be disabled by settings and is only activated "
  184. "by direct user request."
  185. chrome_policy {
  186. UserFeedbackAllowed {
  187. UserFeedbackAllowed: false
  188. }
  189. }
  190. })");
  191. auto resource_request = std::make_unique<network::ResourceRequest>();
  192. resource_request->url = feedback_post_url_;
  193. resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
  194. resource_request->method = "POST";
  195. // Tell feedback server about the variation state of this install.
  196. variations::AppendVariationsHeaderUnknownSignedIn(
  197. feedback_post_url_,
  198. is_off_the_record_ ? variations::InIncognito::kYes
  199. : variations::InIncognito::kNo,
  200. resource_request.get());
  201. if (report_being_dispatched_->has_email()) {
  202. AppendExtraHeadersToUploadRequest(resource_request.get());
  203. }
  204. std::unique_ptr<network::SimpleURLLoader> simple_url_loader =
  205. network::SimpleURLLoader::Create(std::move(resource_request),
  206. traffic_annotation);
  207. network::SimpleURLLoader* simple_url_loader_ptr = simple_url_loader.get();
  208. simple_url_loader->AttachStringForUpload(report_being_dispatched_->data(),
  209. kProtoBufMimeType);
  210. auto it = uploads_in_progress_.insert(uploads_in_progress_.begin(),
  211. std::move(simple_url_loader));
  212. if (!url_loader_factory_) {
  213. // Lazily create the URLLoaderFactory.
  214. url_loader_factory_ = std::move(url_loader_factory_getter_).Run();
  215. DCHECK(url_loader_factory_);
  216. }
  217. simple_url_loader_ptr->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
  218. url_loader_factory_.get(),
  219. base::BindOnce(&FeedbackUploader::OnDispatchComplete,
  220. base::Unretained(this), std::move(it)));
  221. }
  222. void FeedbackUploader::OnDispatchComplete(
  223. UrlLoaderList::iterator it,
  224. std::unique_ptr<std::string> response_body) {
  225. std::stringstream error_stream;
  226. network::SimpleURLLoader* simple_url_loader = it->get();
  227. int response_code = kHttpPostFailNoConnection;
  228. if (simple_url_loader->ResponseInfo() &&
  229. simple_url_loader->ResponseInfo()->headers) {
  230. response_code = simple_url_loader->ResponseInfo()->headers->response_code();
  231. }
  232. if (response_code == kHttpPostSuccessNoContent) {
  233. error_stream << "Success";
  234. OnReportUploadSuccess();
  235. } else {
  236. bool should_retry = true;
  237. // Process the error for debug output
  238. if (response_code == kHttpPostFailNoConnection) {
  239. error_stream << "No connection to server.";
  240. } else if ((response_code >= kHttpPostFailClientError) &&
  241. (response_code < kHttpPostFailServerError)) {
  242. // Client errors mean that the server failed to parse the proto that was
  243. // sent, or that some requirements weren't met by the server side
  244. // validation, and hence we should NOT retry sending this corrupt report
  245. // and give up.
  246. should_retry = false;
  247. error_stream << "Client error: HTTP response code " << response_code;
  248. } else if (response_code >= kHttpPostFailServerError) {
  249. error_stream << "Server error: HTTP response code " << response_code;
  250. } else {
  251. error_stream << "Unknown error: HTTP response code " << response_code;
  252. }
  253. OnReportUploadFailure(should_retry);
  254. }
  255. LOG(WARNING) << "FEEDBACK: Submission to feedback server ("
  256. << simple_url_loader->GetFinalURL()
  257. << ") status: " << error_stream.str();
  258. uploads_in_progress_.erase(it);
  259. }
  260. void FeedbackUploader::UpdateUploadTimer() {
  261. if (reports_queue_.empty())
  262. return;
  263. scoped_refptr<FeedbackReport> report = reports_queue_.top();
  264. const base::Time now = base::Time::Now();
  265. if (report->upload_at() <= now && !is_dispatching_) {
  266. reports_queue_.pop();
  267. is_dispatching_ = true;
  268. report_being_dispatched_ = report;
  269. StartDispatchingReport();
  270. } else {
  271. // Stop the old timer and start an updated one.
  272. const base::TimeDelta delay = (is_dispatching_ || now > report->upload_at())
  273. ? g_dispatching_wait_delay
  274. : report->upload_at() - now;
  275. upload_timer_.Stop();
  276. upload_timer_.Start(FROM_HERE, delay, this,
  277. &FeedbackUploader::UpdateUploadTimer);
  278. }
  279. }
  280. } // namespace feedback