Home | History | Annotate | Download | only in safe_browsing
      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/two_phase_uploader.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/bind.h"
      9 #include "base/task_runner.h"
     10 #include "net/base/net_errors.h"
     11 #include "net/http/http_response_headers.h"
     12 #include "net/url_request/url_fetcher.h"
     13 #include "net/url_request/url_fetcher_delegate.h"
     14 #include "net/url_request/url_request_status.h"
     15 
     16 namespace {
     17 
     18 // Header sent on initial request to start the two phase upload process.
     19 const char* kStartHeader = "x-goog-resumable: start";
     20 
     21 // Header returned on initial response with URL to use for the second phase.
     22 const char* kLocationHeader = "Location";
     23 
     24 const char* kUploadContentType = "application/octet-stream";
     25 
     26 class TwoPhaseUploaderImpl : public net::URLFetcherDelegate,
     27                              public TwoPhaseUploader {
     28  public:
     29   TwoPhaseUploaderImpl(net::URLRequestContextGetter* url_request_context_getter,
     30                        base::TaskRunner* file_task_runner,
     31                        const GURL& base_url,
     32                        const std::string& metadata,
     33                        const base::FilePath& file_path,
     34                        const ProgressCallback& progress_callback,
     35                        const FinishCallback& finish_callback);
     36   virtual ~TwoPhaseUploaderImpl();
     37 
     38   // Begins the upload process.
     39   virtual void Start() OVERRIDE;
     40 
     41   // net::URLFetcherDelegate implementation:
     42   virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
     43   virtual void OnURLFetchUploadProgress(const net::URLFetcher* source,
     44                                         int64 current,
     45                                         int64 total) OVERRIDE;
     46 
     47  private:
     48   void UploadMetadata();
     49   void UploadFile();
     50   void Finish(int net_error, int response_code, const std::string& response);
     51 
     52   State state_;
     53   scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
     54   scoped_refptr<base::TaskRunner> file_task_runner_;
     55   GURL base_url_;
     56   GURL upload_url_;
     57   std::string metadata_;
     58   const base::FilePath file_path_;
     59   ProgressCallback progress_callback_;
     60   FinishCallback finish_callback_;
     61 
     62   scoped_ptr<net::URLFetcher> url_fetcher_;
     63 
     64   DISALLOW_COPY_AND_ASSIGN(TwoPhaseUploaderImpl);
     65 };
     66 
     67 TwoPhaseUploaderImpl::TwoPhaseUploaderImpl(
     68     net::URLRequestContextGetter* url_request_context_getter,
     69     base::TaskRunner* file_task_runner,
     70     const GURL& base_url,
     71     const std::string& metadata,
     72     const base::FilePath& file_path,
     73     const ProgressCallback& progress_callback,
     74     const FinishCallback& finish_callback)
     75     : state_(STATE_NONE),
     76       url_request_context_getter_(url_request_context_getter),
     77       file_task_runner_(file_task_runner),
     78       base_url_(base_url),
     79       metadata_(metadata),
     80       file_path_(file_path),
     81       progress_callback_(progress_callback),
     82       finish_callback_(finish_callback) {
     83 }
     84 
     85 TwoPhaseUploaderImpl::~TwoPhaseUploaderImpl() {
     86   DCHECK(CalledOnValidThread());
     87 }
     88 
     89 void TwoPhaseUploaderImpl::Start() {
     90   DCHECK(CalledOnValidThread());
     91   DCHECK_EQ(STATE_NONE, state_);
     92 
     93   UploadMetadata();
     94 }
     95 
     96 void TwoPhaseUploaderImpl::OnURLFetchComplete(const net::URLFetcher* source) {
     97   DCHECK(CalledOnValidThread());
     98   net::URLRequestStatus status = source->GetStatus();
     99   int response_code = source->GetResponseCode();
    100 
    101   DVLOG(1) << __FUNCTION__ << " " << source->GetURL().spec()
    102            << " " << status.status() << " " << response_code;
    103 
    104   if (!status.is_success()) {
    105     LOG(ERROR) << "URLFetcher failed, status=" << status.status()
    106                << " err=" << status.error();
    107     Finish(status.error(), response_code, std::string());
    108     return;
    109   }
    110 
    111   std::string response;
    112   source->GetResponseAsString(&response);
    113 
    114   switch (state_) {
    115     case UPLOAD_METADATA:
    116       {
    117         if (response_code != 201) {
    118           LOG(ERROR) << "Invalid response to initial request: "
    119                      << response_code;
    120           Finish(net::OK, response_code, response);
    121           return;
    122         }
    123         std::string location;
    124         if (!source->GetResponseHeaders()->EnumerateHeader(
    125               NULL, kLocationHeader, &location)) {
    126           LOG(ERROR) << "no location header";
    127           Finish(net::OK, response_code, std::string());
    128           return;
    129         }
    130         DVLOG(1) << "upload location: " << location;
    131         upload_url_ = GURL(location);
    132         UploadFile();
    133         break;
    134       }
    135     case UPLOAD_FILE:
    136       if (response_code != 200) {
    137           LOG(ERROR) << "Invalid response to upload request: "
    138                      << response_code;
    139       } else {
    140         state_ = STATE_SUCCESS;
    141       }
    142       Finish(net::OK, response_code, response);
    143       return;
    144     default:
    145       NOTREACHED();
    146   };
    147 }
    148 
    149 void TwoPhaseUploaderImpl::OnURLFetchUploadProgress(
    150     const net::URLFetcher* source,
    151     int64 current,
    152     int64 total) {
    153   DCHECK(CalledOnValidThread());
    154   DVLOG(3) << __FUNCTION__ << " " << source->GetURL().spec()
    155            << " " << current << "/" << total;
    156   if (state_ == UPLOAD_FILE && !progress_callback_.is_null())
    157     progress_callback_.Run(current, total);
    158 }
    159 
    160 void TwoPhaseUploaderImpl::UploadMetadata() {
    161   DCHECK(CalledOnValidThread());
    162   state_ = UPLOAD_METADATA;
    163   url_fetcher_.reset(net::URLFetcher::Create(base_url_, net::URLFetcher::POST,
    164                                              this));
    165   url_fetcher_->SetRequestContext(url_request_context_getter_.get());
    166   url_fetcher_->SetExtraRequestHeaders(kStartHeader);
    167   url_fetcher_->SetUploadData(kUploadContentType, metadata_);
    168   url_fetcher_->Start();
    169 }
    170 
    171 void TwoPhaseUploaderImpl::UploadFile() {
    172   DCHECK(CalledOnValidThread());
    173   state_ = UPLOAD_FILE;
    174 
    175   url_fetcher_.reset(net::URLFetcher::Create(upload_url_, net::URLFetcher::PUT,
    176                                              this));
    177   url_fetcher_->SetRequestContext(url_request_context_getter_.get());
    178   url_fetcher_->SetUploadFilePath(
    179       kUploadContentType, file_path_, 0, kuint64max, file_task_runner_);
    180   url_fetcher_->Start();
    181 }
    182 
    183 void TwoPhaseUploaderImpl::Finish(int net_error,
    184                                   int response_code,
    185                                   const std::string& response) {
    186   DCHECK(CalledOnValidThread());
    187   finish_callback_.Run(state_, net_error, response_code, response);
    188 }
    189 
    190 }  // namespace
    191 
    192 // static
    193 TwoPhaseUploaderFactory* TwoPhaseUploader::factory_ = NULL;
    194 
    195 // static
    196 TwoPhaseUploader* TwoPhaseUploader::Create(
    197     net::URLRequestContextGetter* url_request_context_getter,
    198     base::TaskRunner* file_task_runner,
    199     const GURL& base_url,
    200     const std::string& metadata,
    201     const base::FilePath& file_path,
    202     const ProgressCallback& progress_callback,
    203     const FinishCallback& finish_callback) {
    204   if (!TwoPhaseUploader::factory_)
    205     return new TwoPhaseUploaderImpl(
    206         url_request_context_getter, file_task_runner, base_url, metadata,
    207         file_path, progress_callback, finish_callback);
    208   return TwoPhaseUploader::factory_->CreateTwoPhaseUploader(
    209       url_request_context_getter, file_task_runner, base_url, metadata,
    210       file_path, progress_callback, finish_callback);
    211 }
    212