| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/safe_browsing/download_feedback.h" |
| |
| #include "base/bind.h" |
| #include "base/files/file_util_proxy.h" |
| #include "base/metrics/histogram.h" |
| #include "base/task_runner.h" |
| #include "chrome/common/safe_browsing/csd.pb.h" |
| #include "net/base/net_errors.h" |
| |
| namespace safe_browsing { |
| |
| namespace { |
| |
| // This enum is used by histograms. Do not change the ordering or remove items. |
| enum UploadResultType { |
| UPLOAD_SUCCESS, |
| UPLOAD_CANCELLED, |
| UPLOAD_METADATA_NET_ERROR, |
| UPLOAD_METADATA_RESPONSE_ERROR, |
| UPLOAD_FILE_NET_ERROR, |
| UPLOAD_FILE_RESPONSE_ERROR, |
| UPLOAD_COMPLETE_RESPONSE_ERROR, |
| // Memory space for histograms is determined by the max. |
| // ALWAYS ADD NEW VALUES BEFORE THIS ONE. |
| UPLOAD_RESULT_MAX |
| }; |
| |
| // Handles the uploading of a single downloaded binary to the safebrowsing |
| // download feedback service. |
| class DownloadFeedbackImpl : public DownloadFeedback { |
| public: |
| DownloadFeedbackImpl(net::URLRequestContextGetter* request_context_getter, |
| base::TaskRunner* file_task_runner, |
| const base::FilePath& file_path, |
| const std::string& ping_request, |
| const std::string& ping_response); |
| virtual ~DownloadFeedbackImpl(); |
| |
| virtual void Start(const base::Closure& finish_callback) OVERRIDE; |
| |
| virtual const std::string& GetPingRequestForTesting() const OVERRIDE { |
| return ping_request_; |
| } |
| |
| virtual const std::string& GetPingResponseForTesting() const OVERRIDE { |
| return ping_response_; |
| } |
| |
| private: |
| // Callback for TwoPhaseUploader completion. Relays the result to the |
| // |finish_callback|. |
| void FinishedUpload(base::Closure finish_callback, |
| TwoPhaseUploader::State state, |
| int net_error, |
| int response_code, |
| const std::string& response); |
| |
| void RecordUploadResult(UploadResultType result); |
| |
| scoped_refptr<net::URLRequestContextGetter> request_context_getter_; |
| scoped_refptr<base::TaskRunner> file_task_runner_; |
| const base::FilePath file_path_; |
| int64 file_size_; |
| |
| // The safebrowsing request and response of checking that this binary is |
| // unsafe. |
| std::string ping_request_; |
| std::string ping_response_; |
| |
| scoped_ptr<TwoPhaseUploader> uploader_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DownloadFeedbackImpl); |
| }; |
| |
| DownloadFeedbackImpl::DownloadFeedbackImpl( |
| net::URLRequestContextGetter* request_context_getter, |
| base::TaskRunner* file_task_runner, |
| const base::FilePath& file_path, |
| const std::string& ping_request, |
| const std::string& ping_response) |
| : request_context_getter_(request_context_getter), |
| file_task_runner_(file_task_runner), |
| file_path_(file_path), |
| file_size_(-1), |
| ping_request_(ping_request), |
| ping_response_(ping_response) { |
| DVLOG(1) << "DownloadFeedback constructed " << this << " for " |
| << file_path.AsUTF8Unsafe(); |
| } |
| |
| DownloadFeedbackImpl::~DownloadFeedbackImpl() { |
| DCHECK(CalledOnValidThread()); |
| DVLOG(1) << "DownloadFeedback destructed " << this; |
| |
| if (uploader_) { |
| RecordUploadResult(UPLOAD_CANCELLED); |
| // Destroy the uploader before attempting to delete the file. |
| uploader_.reset(); |
| } |
| |
| base::FileUtilProxy::DeleteFile(file_task_runner_.get(), |
| file_path_, |
| false, |
| base::FileUtilProxy::StatusCallback()); |
| } |
| |
| void DownloadFeedbackImpl::Start(const base::Closure& finish_callback) { |
| DCHECK(CalledOnValidThread()); |
| DCHECK(!uploader_); |
| |
| ClientDownloadReport report_metadata; |
| |
| bool r = report_metadata.mutable_download_request()->ParseFromString( |
| ping_request_); |
| DCHECK(r); |
| r = report_metadata.mutable_download_response()->ParseFromString( |
| ping_response_); |
| DCHECK(r); |
| file_size_ = report_metadata.download_request().length(); |
| |
| std::string metadata_string; |
| bool ok = report_metadata.SerializeToString(&metadata_string); |
| DCHECK(ok); |
| uploader_.reset( |
| TwoPhaseUploader::Create(request_context_getter_.get(), |
| file_task_runner_.get(), |
| GURL(kSbFeedbackURL), |
| metadata_string, |
| file_path_, |
| TwoPhaseUploader::ProgressCallback(), |
| base::Bind(&DownloadFeedbackImpl::FinishedUpload, |
| base::Unretained(this), |
| finish_callback))); |
| uploader_->Start(); |
| } |
| |
| void DownloadFeedbackImpl::FinishedUpload(base::Closure finish_callback, |
| TwoPhaseUploader::State state, |
| int net_error, |
| int response_code, |
| const std::string& response_data) { |
| DCHECK(CalledOnValidThread()); |
| DVLOG(1) << __FUNCTION__ << " " << state << " rlen=" << response_data.size(); |
| |
| switch (state) { |
| case TwoPhaseUploader::STATE_SUCCESS: { |
| ClientUploadResponse response; |
| if (!response.ParseFromString(response_data) || |
| response.status() != ClientUploadResponse::SUCCESS) |
| RecordUploadResult(UPLOAD_COMPLETE_RESPONSE_ERROR); |
| else |
| RecordUploadResult(UPLOAD_SUCCESS); |
| break; |
| } |
| case TwoPhaseUploader::UPLOAD_FILE: |
| if (net_error != net::OK) |
| RecordUploadResult(UPLOAD_FILE_NET_ERROR); |
| else |
| RecordUploadResult(UPLOAD_FILE_RESPONSE_ERROR); |
| break; |
| case TwoPhaseUploader::UPLOAD_METADATA: |
| if (net_error != net::OK) |
| RecordUploadResult(UPLOAD_METADATA_NET_ERROR); |
| else |
| RecordUploadResult(UPLOAD_METADATA_RESPONSE_ERROR); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| |
| uploader_.reset(); |
| |
| finish_callback.Run(); |
| // We may be deleted here. |
| } |
| |
| void DownloadFeedbackImpl::RecordUploadResult(UploadResultType result) { |
| if (result == UPLOAD_SUCCESS) |
| UMA_HISTOGRAM_CUSTOM_COUNTS( |
| "SBDownloadFeedback.SizeSuccess", file_size_, 1, kMaxUploadSize, 50); |
| else |
| UMA_HISTOGRAM_CUSTOM_COUNTS( |
| "SBDownloadFeedback.SizeFailure", file_size_, 1, kMaxUploadSize, 50); |
| UMA_HISTOGRAM_ENUMERATION( |
| "SBDownloadFeedback.UploadResult", result, UPLOAD_RESULT_MAX); |
| } |
| |
| } // namespace |
| |
| // static |
| const int64 DownloadFeedback::kMaxUploadSize = 50 * 1024 * 1024; |
| |
| // static |
| const char DownloadFeedback::kSbFeedbackURL[] = |
| "https://safebrowsing.google.com/safebrowsing/uploads/chrome"; |
| |
| // static |
| DownloadFeedbackFactory* DownloadFeedback::factory_ = NULL; |
| |
| // static |
| DownloadFeedback* DownloadFeedback::Create( |
| net::URLRequestContextGetter* request_context_getter, |
| base::TaskRunner* file_task_runner, |
| const base::FilePath& file_path, |
| const std::string& ping_request, |
| const std::string& ping_response) { |
| if (!DownloadFeedback::factory_) |
| return new DownloadFeedbackImpl( |
| request_context_getter, file_task_runner, file_path, ping_request, |
| ping_response); |
| return DownloadFeedback::factory_->CreateDownloadFeedback( |
| request_context_getter, file_task_runner, file_path, ping_request, |
| ping_response); |
| } |
| |
| } // namespace safe_browsing |
| |