download_feedback.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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/safe_browsing/download_feedback.h" 6 7#include "base/bind.h" 8#include "base/command_line.h" 9#include "base/files/file_util_proxy.h" 10#include "base/metrics/histogram.h" 11#include "base/task_runner.h" 12#include "chrome/common/chrome_switches.h" 13#include "chrome/common/safe_browsing/csd.pb.h" 14#include "net/base/net_errors.h" 15 16namespace safe_browsing { 17 18namespace { 19 20// The default URL where browser sends download feedback requests. 21const char kSbDefaultFeedbackURL[] = 22 "https://safebrowsing.google.com/safebrowsing/uploads/chrome"; 23 24// This enum is used by histograms. Do not change the ordering or remove items. 25enum UploadResultType { 26 UPLOAD_SUCCESS, 27 UPLOAD_CANCELLED, 28 UPLOAD_METADATA_NET_ERROR, 29 UPLOAD_METADATA_RESPONSE_ERROR, 30 UPLOAD_FILE_NET_ERROR, 31 UPLOAD_FILE_RESPONSE_ERROR, 32 UPLOAD_COMPLETE_RESPONSE_ERROR, 33 // Memory space for histograms is determined by the max. 34 // ALWAYS ADD NEW VALUES BEFORE THIS ONE. 35 UPLOAD_RESULT_MAX 36}; 37 38// Handles the uploading of a single downloaded binary to the safebrowsing 39// download feedback service. 40class DownloadFeedbackImpl : public DownloadFeedback { 41 public: 42 DownloadFeedbackImpl(net::URLRequestContextGetter* request_context_getter, 43 base::TaskRunner* file_task_runner, 44 const base::FilePath& file_path, 45 const std::string& ping_request, 46 const std::string& ping_response); 47 virtual ~DownloadFeedbackImpl(); 48 49 virtual void Start(const base::Closure& finish_callback) OVERRIDE; 50 51 virtual const std::string& GetPingRequestForTesting() const OVERRIDE { 52 return ping_request_; 53 } 54 55 virtual const std::string& GetPingResponseForTesting() const OVERRIDE { 56 return ping_response_; 57 } 58 59 private: 60 // Callback for TwoPhaseUploader completion. Relays the result to the 61 // |finish_callback|. 62 void FinishedUpload(base::Closure finish_callback, 63 TwoPhaseUploader::State state, 64 int net_error, 65 int response_code, 66 const std::string& response); 67 68 void RecordUploadResult(UploadResultType result); 69 70 scoped_refptr<net::URLRequestContextGetter> request_context_getter_; 71 scoped_refptr<base::TaskRunner> file_task_runner_; 72 const base::FilePath file_path_; 73 int64 file_size_; 74 75 // The safebrowsing request and response of checking that this binary is 76 // unsafe. 77 std::string ping_request_; 78 std::string ping_response_; 79 80 scoped_ptr<TwoPhaseUploader> uploader_; 81 82 DISALLOW_COPY_AND_ASSIGN(DownloadFeedbackImpl); 83}; 84 85DownloadFeedbackImpl::DownloadFeedbackImpl( 86 net::URLRequestContextGetter* request_context_getter, 87 base::TaskRunner* file_task_runner, 88 const base::FilePath& file_path, 89 const std::string& ping_request, 90 const std::string& ping_response) 91 : request_context_getter_(request_context_getter), 92 file_task_runner_(file_task_runner), 93 file_path_(file_path), 94 file_size_(-1), 95 ping_request_(ping_request), 96 ping_response_(ping_response) { 97 DVLOG(1) << "DownloadFeedback constructed " << this << " for " 98 << file_path.AsUTF8Unsafe(); 99} 100 101DownloadFeedbackImpl::~DownloadFeedbackImpl() { 102 DCHECK(CalledOnValidThread()); 103 DVLOG(1) << "DownloadFeedback destructed " << this; 104 105 if (uploader_) { 106 RecordUploadResult(UPLOAD_CANCELLED); 107 // Destroy the uploader before attempting to delete the file. 108 uploader_.reset(); 109 } 110 111 base::FileUtilProxy::DeleteFile(file_task_runner_.get(), 112 file_path_, 113 false, 114 base::FileUtilProxy::StatusCallback()); 115} 116 117void DownloadFeedbackImpl::Start(const base::Closure& finish_callback) { 118 DCHECK(CalledOnValidThread()); 119 DCHECK(!uploader_); 120 121 CommandLine* cmdline = CommandLine::ForCurrentProcess(); 122 123 ClientDownloadReport report_metadata; 124 125 bool r = report_metadata.mutable_download_request()->ParseFromString( 126 ping_request_); 127 DCHECK(r); 128 r = report_metadata.mutable_download_response()->ParseFromString( 129 ping_response_); 130 DCHECK(r); 131 file_size_ = report_metadata.download_request().length(); 132 133 GURL url = GURL(cmdline->HasSwitch(switches::kSbDownloadFeedbackURL) ? 134 cmdline->GetSwitchValueASCII(switches::kSbDownloadFeedbackURL) : 135 kSbDefaultFeedbackURL); 136 137 std::string metadata_string; 138 bool ok = report_metadata.SerializeToString(&metadata_string); 139 DCHECK(ok); 140 uploader_.reset( 141 TwoPhaseUploader::Create(request_context_getter_.get(), 142 file_task_runner_.get(), 143 url, 144 metadata_string, 145 file_path_, 146 TwoPhaseUploader::ProgressCallback(), 147 base::Bind(&DownloadFeedbackImpl::FinishedUpload, 148 base::Unretained(this), 149 finish_callback))); 150 uploader_->Start(); 151} 152 153void DownloadFeedbackImpl::FinishedUpload(base::Closure finish_callback, 154 TwoPhaseUploader::State state, 155 int net_error, 156 int response_code, 157 const std::string& response_data) { 158 DCHECK(CalledOnValidThread()); 159 DVLOG(1) << __FUNCTION__ << " " << state << " rlen=" << response_data.size(); 160 161 switch (state) { 162 case TwoPhaseUploader::STATE_SUCCESS: { 163 ClientUploadResponse response; 164 if (!response.ParseFromString(response_data) || 165 response.status() != ClientUploadResponse::SUCCESS) 166 RecordUploadResult(UPLOAD_COMPLETE_RESPONSE_ERROR); 167 else 168 RecordUploadResult(UPLOAD_SUCCESS); 169 break; 170 } 171 case TwoPhaseUploader::UPLOAD_FILE: 172 if (net_error != net::OK) 173 RecordUploadResult(UPLOAD_FILE_NET_ERROR); 174 else 175 RecordUploadResult(UPLOAD_FILE_RESPONSE_ERROR); 176 break; 177 case TwoPhaseUploader::UPLOAD_METADATA: 178 if (net_error != net::OK) 179 RecordUploadResult(UPLOAD_METADATA_NET_ERROR); 180 else 181 RecordUploadResult(UPLOAD_METADATA_RESPONSE_ERROR); 182 break; 183 default: 184 NOTREACHED(); 185 } 186 187 uploader_.reset(); 188 189 finish_callback.Run(); 190 // We may be deleted here. 191} 192 193void DownloadFeedbackImpl::RecordUploadResult(UploadResultType result) { 194 if (result == UPLOAD_SUCCESS) 195 UMA_HISTOGRAM_CUSTOM_COUNTS( 196 "SBDownloadFeedback.SizeSuccess", file_size_, 1, kMaxUploadSize, 50); 197 else 198 UMA_HISTOGRAM_CUSTOM_COUNTS( 199 "SBDownloadFeedback.SizeFailure", file_size_, 1, kMaxUploadSize, 50); 200 UMA_HISTOGRAM_ENUMERATION( 201 "SBDownloadFeedback.UploadResult", result, UPLOAD_RESULT_MAX); 202} 203 204} // namespace 205 206// static 207const int64 DownloadFeedback::kMaxUploadSize = 50 * 1024 * 1024; 208 209// static 210DownloadFeedbackFactory* DownloadFeedback::factory_ = NULL; 211 212// static 213DownloadFeedback* DownloadFeedback::Create( 214 net::URLRequestContextGetter* request_context_getter, 215 base::TaskRunner* file_task_runner, 216 const base::FilePath& file_path, 217 const std::string& ping_request, 218 const std::string& ping_response) { 219 if (!DownloadFeedback::factory_) 220 return new DownloadFeedbackImpl( 221 request_context_getter, file_task_runner, file_path, ping_request, 222 ping_response); 223 return DownloadFeedback::factory_->CreateDownloadFeedback( 224 request_context_getter, file_task_runner, file_path, ping_request, 225 ping_response); 226} 227 228} // namespace safe_browsing 229 230