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