12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/safe_browsing/two_phase_uploader.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/basictypes.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/bind.h"
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/task_runner.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "net/base/net_errors.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "net/http/http_response_headers.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "net/url_request/url_fetcher.h"
13b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "net/url_request/url_fetcher_delegate.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "net/url_request/url_request_status.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Header sent on initial request to start the two phase upload process.
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const char* kStartHeader = "x-goog-resumable: start";
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Header returned on initial response with URL to use for the second phase.
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const char* kLocationHeader = "Location";
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const char* kUploadContentType = "application/octet-stream";
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
26b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)class TwoPhaseUploaderImpl : public net::URLFetcherDelegate,
27b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                             public TwoPhaseUploader {
28b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) public:
29b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  TwoPhaseUploaderImpl(net::URLRequestContextGetter* url_request_context_getter,
30b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                       base::TaskRunner* file_task_runner,
31b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                       const GURL& base_url,
32b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                       const std::string& metadata,
33b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                       const base::FilePath& file_path,
34b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                       const ProgressCallback& progress_callback,
35b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                       const FinishCallback& finish_callback);
36b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  virtual ~TwoPhaseUploaderImpl();
37b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
38b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Begins the upload process.
39b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  virtual void Start() OVERRIDE;
40b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
41b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // net::URLFetcherDelegate implementation:
42b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
43b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  virtual void OnURLFetchUploadProgress(const net::URLFetcher* source,
44b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                                        int64 current,
45b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                                        int64 total) OVERRIDE;
46b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
47b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) private:
48b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  void UploadMetadata();
49b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  void UploadFile();
50b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  void Finish(int net_error, int response_code, const std::string& response);
51b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
52b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  State state_;
53b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
54b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  scoped_refptr<base::TaskRunner> file_task_runner_;
55b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  GURL base_url_;
56b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  GURL upload_url_;
57b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  std::string metadata_;
58b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  const base::FilePath file_path_;
59b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  ProgressCallback progress_callback_;
60b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  FinishCallback finish_callback_;
61b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
62b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  scoped_ptr<net::URLFetcher> url_fetcher_;
63b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
64b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(TwoPhaseUploaderImpl);
65b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)};
66b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
67b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)TwoPhaseUploaderImpl::TwoPhaseUploaderImpl(
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    net::URLRequestContextGetter* url_request_context_getter,
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::TaskRunner* file_task_runner,
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const GURL& base_url,
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& metadata,
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& file_path,
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const ProgressCallback& progress_callback,
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const FinishCallback& finish_callback)
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : state_(STATE_NONE),
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      url_request_context_getter_(url_request_context_getter),
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      file_task_runner_(file_task_runner),
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base_url_(base_url),
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      metadata_(metadata),
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      file_path_(file_path),
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      progress_callback_(progress_callback),
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      finish_callback_(finish_callback) {
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
85b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)TwoPhaseUploaderImpl::~TwoPhaseUploaderImpl() {
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(CalledOnValidThread());
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
89b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void TwoPhaseUploaderImpl::Start() {
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(CalledOnValidThread());
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(STATE_NONE, state_);
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  UploadMetadata();
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
96b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void TwoPhaseUploaderImpl::OnURLFetchComplete(const net::URLFetcher* source) {
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(CalledOnValidThread());
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  net::URLRequestStatus status = source->GetStatus();
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int response_code = source->GetResponseCode();
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(1) << __FUNCTION__ << " " << source->GetURL().spec()
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           << " " << status.status() << " " << response_code;
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!status.is_success()) {
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    LOG(ERROR) << "URLFetcher failed, status=" << status.status()
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)               << " err=" << status.error();
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    Finish(status.error(), response_code, std::string());
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string response;
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  source->GetResponseAsString(&response);
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  switch (state_) {
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case UPLOAD_METADATA:
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      {
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (response_code != 201) {
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          LOG(ERROR) << "Invalid response to initial request: "
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                     << response_code;
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          Finish(net::OK, response_code, response);
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return;
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        std::string location;
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!source->GetResponseHeaders()->EnumerateHeader(
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              NULL, kLocationHeader, &location)) {
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          LOG(ERROR) << "no location header";
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          Finish(net::OK, response_code, std::string());
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return;
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        DVLOG(1) << "upload location: " << location;
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        upload_url_ = GURL(location);
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        UploadFile();
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        break;
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case UPLOAD_FILE:
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (response_code != 200) {
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          LOG(ERROR) << "Invalid response to upload request: "
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                     << response_code;
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        state_ = STATE_SUCCESS;
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      Finish(net::OK, response_code, response);
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    default:
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      NOTREACHED();
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  };
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
149b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void TwoPhaseUploaderImpl::OnURLFetchUploadProgress(
150b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    const net::URLFetcher* source,
151b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    int64 current,
152b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    int64 total) {
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(CalledOnValidThread());
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(3) << __FUNCTION__ << " " << source->GetURL().spec()
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           << " " << current << "/" << total;
1567d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (state_ == UPLOAD_FILE && !progress_callback_.is_null())
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    progress_callback_.Run(current, total);
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
160b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void TwoPhaseUploaderImpl::UploadMetadata() {
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(CalledOnValidThread());
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  state_ = UPLOAD_METADATA;
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  url_fetcher_.reset(net::URLFetcher::Create(base_url_, net::URLFetcher::POST,
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             this));
165868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  url_fetcher_->SetRequestContext(url_request_context_getter_.get());
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  url_fetcher_->SetExtraRequestHeaders(kStartHeader);
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  url_fetcher_->SetUploadData(kUploadContentType, metadata_);
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  url_fetcher_->Start();
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
171b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void TwoPhaseUploaderImpl::UploadFile() {
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(CalledOnValidThread());
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  state_ = UPLOAD_FILE;
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  url_fetcher_.reset(net::URLFetcher::Create(upload_url_, net::URLFetcher::PUT,
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             this));
177868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  url_fetcher_->SetRequestContext(url_request_context_getter_.get());
178868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  url_fetcher_->SetUploadFilePath(
179868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      kUploadContentType, file_path_, 0, kuint64max, file_task_runner_);
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  url_fetcher_->Start();
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
183b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void TwoPhaseUploaderImpl::Finish(int net_error,
184b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                                  int response_code,
185b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                                  const std::string& response) {
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(CalledOnValidThread());
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  finish_callback_.Run(state_, net_error, response_code, response);
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
189b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
190b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}  // namespace
191b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
192b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// static
193b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)TwoPhaseUploaderFactory* TwoPhaseUploader::factory_ = NULL;
194b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
195b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// static
196b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)TwoPhaseUploader* TwoPhaseUploader::Create(
197b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    net::URLRequestContextGetter* url_request_context_getter,
198b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    base::TaskRunner* file_task_runner,
199b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    const GURL& base_url,
200b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    const std::string& metadata,
201b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    const base::FilePath& file_path,
202b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    const ProgressCallback& progress_callback,
203b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    const FinishCallback& finish_callback) {
204b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (!TwoPhaseUploader::factory_)
205b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return new TwoPhaseUploaderImpl(
206b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        url_request_context_getter, file_task_runner, base_url, metadata,
207b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        file_path, progress_callback, finish_callback);
208b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  return TwoPhaseUploader::factory_->CreateTwoPhaseUploader(
209b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      url_request_context_getter, file_task_runner, base_url, metadata,
210b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      file_path, progress_callback, finish_callback);
211b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
212