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