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 5#include "components/component_updater/component_updater_ping_manager.h" 6 7#include <string> 8#include <vector> 9 10#include "base/bind.h" 11#include "base/bind_helpers.h" 12#include "base/compiler_specific.h" 13#include "base/location.h" 14#include "base/logging.h" 15#include "base/macros.h" 16#include "base/memory/scoped_ptr.h" 17#include "base/sequenced_task_runner.h" 18#include "base/strings/string_number_conversions.h" 19#include "base/strings/string_util.h" 20#include "base/strings/stringprintf.h" 21#include "base/threading/thread_checker.h" 22#include "components/component_updater/component_updater_configurator.h" 23#include "components/component_updater/component_updater_utils.h" 24#include "components/component_updater/crx_update_item.h" 25#include "components/component_updater/request_sender.h" 26#include "url/gurl.h" 27 28namespace net { 29class URLFetcher; 30} // namespace net 31 32namespace component_updater { 33 34namespace { 35 36// Returns a string literal corresponding to the value of the downloader |d|. 37const char* DownloaderToString(CrxDownloader::DownloadMetrics::Downloader d) { 38 switch (d) { 39 case CrxDownloader::DownloadMetrics::kUrlFetcher: 40 return "direct"; 41 case CrxDownloader::DownloadMetrics::kBits: 42 return "bits"; 43 default: 44 return "unknown"; 45 } 46} 47 48// Returns a string representing a sequence of download complete events 49// corresponding to each download metrics in |item|. 50std::string BuildDownloadCompleteEventElements(const CrxUpdateItem* item) { 51 using base::StringAppendF; 52 std::string download_events; 53 for (size_t i = 0; i != item->download_metrics.size(); ++i) { 54 const CrxDownloader::DownloadMetrics& metrics = item->download_metrics[i]; 55 std::string event("<event eventtype=\"14\""); 56 StringAppendF(&event, " eventresult=\"%d\"", metrics.error == 0); 57 StringAppendF(&event, 58 " downloader=\"%s\"", 59 DownloaderToString(metrics.downloader)); 60 if (metrics.error) { 61 StringAppendF(&event, " errorcode=\"%d\"", metrics.error); 62 } 63 StringAppendF(&event, " url=\"%s\"", metrics.url.spec().c_str()); 64 65 // -1 means that the byte counts are not known. 66 if (metrics.downloaded_bytes != -1) { 67 StringAppendF(&event, 68 " downloaded=\"%s\"", 69 base::Int64ToString(metrics.downloaded_bytes).c_str()); 70 } 71 if (metrics.total_bytes != -1) { 72 StringAppendF(&event, 73 " total=\"%s\"", 74 base::Int64ToString(metrics.total_bytes).c_str()); 75 } 76 77 if (metrics.download_time_ms) { 78 StringAppendF(&event, 79 " download_time_ms=\"%s\"", 80 base::Uint64ToString(metrics.download_time_ms).c_str()); 81 } 82 StringAppendF(&event, "/>"); 83 84 download_events += event; 85 } 86 return download_events; 87} 88 89// Returns a string representing one ping event xml element for an update item. 90std::string BuildUpdateCompleteEventElement(const CrxUpdateItem* item) { 91 DCHECK(item->status == CrxUpdateItem::kNoUpdate || 92 item->status == CrxUpdateItem::kUpdated); 93 94 using base::StringAppendF; 95 96 std::string ping_event("<event eventtype=\"3\""); 97 const int event_result = item->status == CrxUpdateItem::kUpdated; 98 StringAppendF(&ping_event, " eventresult=\"%d\"", event_result); 99 if (item->error_category) 100 StringAppendF(&ping_event, " errorcat=\"%d\"", item->error_category); 101 if (item->error_code) 102 StringAppendF(&ping_event, " errorcode=\"%d\"", item->error_code); 103 if (item->extra_code1) 104 StringAppendF(&ping_event, " extracode1=\"%d\"", item->extra_code1); 105 if (HasDiffUpdate(item)) 106 StringAppendF(&ping_event, " diffresult=\"%d\"", !item->diff_update_failed); 107 if (item->diff_error_category) { 108 StringAppendF( 109 &ping_event, " differrorcat=\"%d\"", item->diff_error_category); 110 } 111 if (item->diff_error_code) 112 StringAppendF(&ping_event, " differrorcode=\"%d\"", item->diff_error_code); 113 if (item->diff_extra_code1) { 114 StringAppendF( 115 &ping_event, " diffextracode1=\"%d\"", item->diff_extra_code1); 116 } 117 if (!item->previous_fp.empty()) 118 StringAppendF(&ping_event, " previousfp=\"%s\"", item->previous_fp.c_str()); 119 if (!item->next_fp.empty()) 120 StringAppendF(&ping_event, " nextfp=\"%s\"", item->next_fp.c_str()); 121 StringAppendF(&ping_event, "/>"); 122 return ping_event; 123} 124 125// Builds a ping message for the specified update item. 126std::string BuildPing(const Configurator& config, const CrxUpdateItem* item) { 127 const char app_element_format[] = 128 "<app appid=\"%s\" version=\"%s\" nextversion=\"%s\">" 129 "%s" 130 "%s" 131 "</app>"; 132 const std::string app_element(base::StringPrintf( 133 app_element_format, 134 item->id.c_str(), // "appid" 135 item->previous_version.GetString().c_str(), // "version" 136 item->next_version.GetString().c_str(), // "nextversion" 137 BuildUpdateCompleteEventElement(item).c_str(), // update event 138 BuildDownloadCompleteEventElements(item).c_str())); // download events 139 140 return BuildProtocolRequest(config.GetBrowserVersion().GetString(), 141 config.GetChannel(), 142 config.GetLang(), 143 config.GetOSLongName(), 144 app_element, 145 ""); 146} 147 148// Sends a fire and forget ping. The instances of this class have no 149// ownership and they self-delete upon completion. One instance of this class 150// can send only one ping. 151class PingSender { 152 public: 153 explicit PingSender(const Configurator& config); 154 ~PingSender(); 155 156 bool SendPing(const CrxUpdateItem* item); 157 158 private: 159 void OnRequestSenderComplete(const net::URLFetcher* source); 160 161 const Configurator& config_; 162 scoped_ptr<RequestSender> request_sender_; 163 base::ThreadChecker thread_checker_; 164 165 DISALLOW_COPY_AND_ASSIGN(PingSender); 166}; 167 168PingSender::PingSender(const Configurator& config) : config_(config) { 169} 170 171PingSender::~PingSender() { 172 DCHECK(thread_checker_.CalledOnValidThread()); 173} 174 175void PingSender::OnRequestSenderComplete(const net::URLFetcher* source) { 176 DCHECK(thread_checker_.CalledOnValidThread()); 177 delete this; 178} 179 180bool PingSender::SendPing(const CrxUpdateItem* item) { 181 DCHECK(item); 182 DCHECK(thread_checker_.CalledOnValidThread()); 183 184 std::vector<GURL> urls(config_.PingUrl()); 185 186 if (urls.empty()) 187 return false; 188 189 request_sender_.reset(new RequestSender(config_)); 190 request_sender_->Send( 191 BuildPing(config_, item), 192 urls, 193 base::Bind(&PingSender::OnRequestSenderComplete, base::Unretained(this))); 194 return true; 195} 196 197} // namespace 198 199PingManager::PingManager(const Configurator& config) : config_(config) { 200} 201 202PingManager::~PingManager() { 203} 204 205// Sends a fire and forget ping when the updates are complete. The ping 206// sender object self-deletes after sending the ping has completed asynchrously. 207void PingManager::OnUpdateComplete(const CrxUpdateItem* item) { 208 PingSender* ping_sender(new PingSender(config_)); 209 if (!ping_sender->SendPing(item)) 210 delete ping_sender; 211} 212 213} // namespace component_updater 214