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.
5 #include "chrome/browser/safe_browsing/two_phase_uploader.h"
7 #include "base/basictypes.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"
18 // Header sent on initial request to start the two phase upload process.
19 const char* kStartHeader = "x-goog-resumable: start";
21 // Header returned on initial response with URL to use for the second phase.
22 const char* kLocationHeader = "Location";
24 const char* kUploadContentType = "application/octet-stream";
26 class TwoPhaseUploaderImpl : public net::URLFetcherDelegate,
27 public TwoPhaseUploader {
29 TwoPhaseUploaderImpl(net::URLRequestContextGetter* url_request_context_getter,
30 base::TaskRunner* file_task_runner,
32 const std::string& metadata,
33 const base::FilePath& file_path,
34 const ProgressCallback& progress_callback,
35 const FinishCallback& finish_callback);
36 virtual ~TwoPhaseUploaderImpl();
38 // Begins the upload process.
39 virtual void Start() OVERRIDE;
41 // net::URLFetcherDelegate implementation:
42 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
43 virtual void OnURLFetchUploadProgress(const net::URLFetcher* source,
45 int64 total) OVERRIDE;
48 void UploadMetadata();
50 void Finish(int net_error, int response_code, const std::string& response);
53 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
54 scoped_refptr<base::TaskRunner> file_task_runner_;
57 std::string metadata_;
58 const base::FilePath file_path_;
59 ProgressCallback progress_callback_;
60 FinishCallback finish_callback_;
62 scoped_ptr<net::URLFetcher> url_fetcher_;
64 DISALLOW_COPY_AND_ASSIGN(TwoPhaseUploaderImpl);
67 TwoPhaseUploaderImpl::TwoPhaseUploaderImpl(
68 net::URLRequestContextGetter* url_request_context_getter,
69 base::TaskRunner* file_task_runner,
71 const std::string& metadata,
72 const base::FilePath& file_path,
73 const ProgressCallback& progress_callback,
74 const FinishCallback& finish_callback)
76 url_request_context_getter_(url_request_context_getter),
77 file_task_runner_(file_task_runner),
80 file_path_(file_path),
81 progress_callback_(progress_callback),
82 finish_callback_(finish_callback) {
85 TwoPhaseUploaderImpl::~TwoPhaseUploaderImpl() {
86 DCHECK(CalledOnValidThread());
89 void TwoPhaseUploaderImpl::Start() {
90 DCHECK(CalledOnValidThread());
91 DCHECK_EQ(STATE_NONE, state_);
96 void TwoPhaseUploaderImpl::OnURLFetchComplete(const net::URLFetcher* source) {
97 DCHECK(CalledOnValidThread());
98 net::URLRequestStatus status = source->GetStatus();
99 int response_code = source->GetResponseCode();
101 DVLOG(1) << __FUNCTION__ << " " << source->GetURL().spec()
102 << " " << status.status() << " " << response_code;
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());
111 std::string response;
112 source->GetResponseAsString(&response);
115 case UPLOAD_METADATA:
117 if (response_code != 201) {
118 LOG(ERROR) << "Invalid response to initial request: "
120 Finish(net::OK, response_code, response);
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());
130 DVLOG(1) << "upload location: " << location;
131 upload_url_ = GURL(location);
136 if (response_code != 200) {
137 LOG(ERROR) << "Invalid response to upload request: "
140 state_ = STATE_SUCCESS;
142 Finish(net::OK, response_code, response);
149 void TwoPhaseUploaderImpl::OnURLFetchUploadProgress(
150 const net::URLFetcher* source,
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);
160 void TwoPhaseUploaderImpl::UploadMetadata() {
161 DCHECK(CalledOnValidThread());
162 state_ = UPLOAD_METADATA;
163 url_fetcher_.reset(net::URLFetcher::Create(base_url_, net::URLFetcher::POST,
165 url_fetcher_->SetRequestContext(url_request_context_getter_.get());
166 url_fetcher_->SetExtraRequestHeaders(kStartHeader);
167 url_fetcher_->SetUploadData(kUploadContentType, metadata_);
168 url_fetcher_->Start();
171 void TwoPhaseUploaderImpl::UploadFile() {
172 DCHECK(CalledOnValidThread());
173 state_ = UPLOAD_FILE;
175 url_fetcher_.reset(net::URLFetcher::Create(upload_url_, net::URLFetcher::PUT,
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();
183 void TwoPhaseUploaderImpl::Finish(int net_error,
185 const std::string& response) {
186 DCHECK(CalledOnValidThread());
187 finish_callback_.Run(state_, net_error, response_code, response);
193 TwoPhaseUploaderFactory* TwoPhaseUploader::factory_ = NULL;
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);