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 ~TwoPhaseUploaderImpl() override
;
38 // Begins the upload process.
39 void Start() override
;
41 // net::URLFetcherDelegate implementation:
42 void OnURLFetchComplete(const net::URLFetcher
* source
) override
;
43 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
;
164 net::URLFetcher::Create(base_url_
, net::URLFetcher::POST
, 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();
171 void TwoPhaseUploaderImpl::UploadFile() {
172 DCHECK(CalledOnValidThread());
173 state_
= UPLOAD_FILE
;
176 net::URLFetcher::Create(upload_url_
, net::URLFetcher::PUT
, 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();
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
);